correção de bugs na area de Dashbord

This commit is contained in:
2026-08-21 01:12:47 -03:00
parent cebd6e2dcd
commit 4965931c13
7 changed files with 361 additions and 41 deletions

View File

@@ -0,0 +1,42 @@
<%# Lista do ranking de motoristas — markup COMPARTILHADO pelas abas "Estimado" e
"Consolidado" do dashboard. Os dois conjuntos têm a mesma forma
(:nome, :valor, :entregas); só muda a origem dos números, então duplicar o
markup faria as duas abas divergirem visualmente na primeira alteração.
Locais: itens (array de hashes), vazio (texto do estado sem dados). %>
<% if itens.any? %>
<div class="space-y-3">
<%# Base da barra: o MAIOR nº de entregas da aba, não o primeiro item — a aba
consolidada é ordenada por VALOR, então o primeiro pode não ser o maior em
quantidade e a barra passaria de 100%. Sem entregas (só bônus/desconto,
por exemplo) a barra fica vazia em vez de dividir por zero. %>
<% max = itens.map { |x| x[:entregas].to_i }.max.to_f %>
<% itens.each_with_index do |m, i| %>
<div class="flex items-center gap-3">
<span class="text-xs font-bold w-5 text-center
<%= i == 0 ? 'text-yellow-400' : i == 1 ? 'text-gray-300' : i == 2 ? 'text-orange-600' : 'text-gray-400' %>">
<%= i + 1 %>
</span>
<div class="flex-1 min-w-0">
<div class="flex items-center justify-between mb-1">
<span class="text-white text-sm font-medium truncate"><%= m[:nome] %></span>
<span class="text-[#f97316] text-sm font-semibold ml-2 flex-shrink-0">
<%= moeda(m[:valor]) %>
</span>
</div>
<%# Barra de progresso %>
<div class="w-full bg-white/5 rounded-full h-1.5">
<div class="h-1.5 rounded-full bg-[#f97316]"
style="width: <%= max.zero? ? 0 : [(m[:entregas] / max * 100).round, 100].min %>%"></div>
</div>
<span class="text-gray-400 text-xs"><%= m[:entregas] %> entregas</span>
</div>
</div>
<% end %>
</div>
<% else %>
<div class="text-center py-8 text-gray-400">
<div class="mb-2"><%= icone :caminhao, tamanho: 'w-8 h-8', cor: 'text-gray-600', espaco: false %></div>
<p class="text-sm"><%= vazio %></p>
</div>
<% end %>

View File

@@ -262,42 +262,48 @@
<% end %>
</div>
<%# Top motoristas %>
<%# Top motoristas — DUAS ABAS sobre o mesmo ranking:
• Estimado = entregas atendidas × preço da entrega (mesma base do card
laranja do topo).
• Consolidado = o que foi REALMENTE fechado nas consolidações do período,
já com bônus/desconto/retirada aplicados, e a quantidade
exata de entregas que entrou no fechamento.
Os dois números divergem DE PROPÓSITO: o estimado cobre tudo que foi
atendido no período; o consolidado, só o que já entrou em consolidação
finalizada. Por isso cada aba diz de onde vem o seu número. %>
<div class="bg-[#1a1a1a] rounded-2xl border border-white/5 p-6">
<h2 class="text-lg font-semibold text-white mb-4 flex items-center gap-2"><%= icone :ranking, espaco: false %> Motoristas</h2>
<div class="flex flex-wrap items-center justify-between gap-2 mb-4">
<h2 class="text-lg font-semibold text-white flex items-center gap-2"><%= icone :ranking, espaco: false %> Motoristas</h2>
<% if @motoristas.any? %>
<div class="space-y-3">
<% @motoristas.each_with_index do |m, i| %>
<div class="flex items-center gap-3">
<span class="text-xs font-bold w-5 text-center
<%= i == 0 ? 'text-yellow-400' : i == 1 ? 'text-gray-300' : i == 2 ? 'text-orange-600' : 'text-gray-400' %>">
<%= i + 1 %>
</span>
<div class="flex-1 min-w-0">
<div class="flex items-center justify-between mb-1">
<span class="text-white text-sm font-medium truncate"><%= m[:nome] %></span>
<span class="text-[#f97316] text-sm font-semibold ml-2 flex-shrink-0">
<%= moeda(m[:valor]) %>
</span>
</div>
<%# Barra de progresso %>
<div class="w-full bg-white/5 rounded-full h-1.5">
<% max = @motoristas.first[:entregas].to_f %>
<div class="h-1.5 rounded-full bg-[#f97316]"
style="width: <%= [(m[:entregas] / max * 100).round, 100].min %>%"></div>
</div>
<span class="text-gray-400 text-xs"><%= m[:entregas] %> entregas</span>
</div>
</div>
<% end %>
<%# Mesmas pílulas da paginação de pagamentos (as classes estão duplicadas
no JS que troca a aba — mexeu aqui, mexa lá). %>
<div id="ranking-abas" class="flex gap-1.5">
<button type="button" data-aba="estimado"
class="px-3 py-1.5 rounded-lg text-xs font-semibold transition-colors bg-[#f97316] text-white">
Estimado
</button>
<button type="button" data-aba="consolidado"
class="px-3 py-1.5 rounded-lg text-xs font-semibold transition-colors bg-[#0a0a0a] text-gray-400 border border-white/5 hover:text-white">
Consolidado
</button>
</div>
<% else %>
<div class="text-center py-8 text-gray-400">
<div class="mb-2"><%= icone :caminhao, tamanho: 'w-8 h-8', cor: 'text-gray-600', espaco: false %></div>
<p class="text-sm">Nenhuma entrega concluída neste período</p>
</div>
<% end %>
</div>
<div id="ranking-painel-estimado">
<p class="text-gray-400 text-xs mb-3">Entregas atendidas × preço da entrega</p>
<%= render 'ranking_motoristas',
itens: @motoristas,
vazio: 'Nenhuma entrega atendida neste período' %>
</div>
<%# Sem JS a aba fica escondida — aceitável: o mesmo dado consolidado
também aparece no gráfico "Custo por motorista" logo abaixo. %>
<div id="ranking-painel-consolidado" class="hidden">
<p class="text-gray-400 text-xs mb-3">Valor fechado nas consolidações do período</p>
<%= render 'ranking_motoristas',
itens: @ranking_consolidado,
vazio: 'Nenhuma consolidação finalizada neste período' %>
</div>
</div>
</div>
@@ -577,6 +583,38 @@
})();
</script>
<%# Abas do ranking de motoristas (Estimado × Consolidado) %>
<script>
(function () {
// As classes repetem as do ERB porque a troca de aba reescreve o className
// inteiro — mesmo padrão da paginação de pagamentos acima.
const ATIVA = 'px-3 py-1.5 rounded-lg text-xs font-semibold transition-colors bg-[#f97316] text-white';
const INATIVA = 'px-3 py-1.5 rounded-lg text-xs font-semibold transition-colors bg-[#0a0a0a] text-gray-400 border border-white/5 hover:text-white';
function initAbasRanking() {
const abas = document.getElementById('ranking-abas');
if (!abas || abas.dataset.iniciado) return;
abas.dataset.iniciado = '1';
const botoes = Array.from(abas.querySelectorAll('button'));
botoes.forEach(function (btn) {
btn.addEventListener('click', function () {
botoes.forEach(function (b) {
const ativa = (b === btn);
const painel = document.getElementById('ranking-painel-' + b.dataset.aba);
b.className = ativa ? ATIVA : INATIVA;
if (painel) painel.classList.toggle('hidden', !ativa);
});
});
});
}
initAbasRanking();
document.addEventListener('turbo:load', initAbasRanking);
})();
</script>
<%# Filtro de operação — submete o form ao marcar/desmarcar %>
<script>
(function () {

View File

@@ -36,8 +36,15 @@
<p class="text-black/80 font-bold uppercase tracking-wide flex items-center gap-2"><%= icone :dinheiro, cor: nil, espaco: false %> Valor estimado deste mês</p>
<p class="text-black/70 text-sm mt-1"><%= icone :calendario, cor: nil %> <%= l @estimado_inicio, format: :short %> a <%= l @estimado_fim, format: :short %></p>
<p class="text-black font-black text-4xl sm:text-5xl mt-2 leading-tight whitespace-nowrap"><%= moeda(@valor_estimado) %></p>
<%# "Atendidas" = concluídas + sem sucesso. O rótulo NÃO pode voltar a dizer
"feitas e confirmadas": o valor acima inclui as visitas sem sucesso, que
a empresa também paga (o motorista foi ao local). Sem a segunda linha o
motorista não consegue conferir a conta. %>
<p class="text-black/80 text-base mt-2">
<%= @entregas_mes %> entregas feitas e confirmadas
<%= @entregas_mes %> entregas atendidas
</p>
<p class="text-black/70 text-sm mt-1">
<%= @entregas_concluidas %> entregues · <%= @entregas_falhadas %> sem sucesso (pagas também)
</p>
<p class="text-black/60 text-sm mt-3">
<%= icone :alerta, cor: nil %> Valor aproximado. O valor certo sai quando a empresa fechar o pagamento.