Correção de alguns valores no dash principal
This commit is contained in:
81
app/views/operacoes_dashboard/_fora_operacao.html.erb
Normal file
81
app/views/operacoes_dashboard/_fora_operacao.html.erb
Normal file
@@ -0,0 +1,81 @@
|
||||
<%# locals: fora (Analytics::NotasForaOperacao), inicio, fim (Date)
|
||||
NFs entregues no período que não constam em nenhuma planilha de operação —
|
||||
os planos avulsos / de inclusão. Contam no dashboard financeiro e antes
|
||||
sumiam daqui (o INNER JOIN com a tabela da operação as descartava). %>
|
||||
<div class="bg-[#1a1a1a] rounded-2xl border border-white/5 overflow-hidden">
|
||||
<div class="flex flex-wrap items-center justify-between gap-3 px-6 py-4 border-b border-white/5">
|
||||
<div>
|
||||
<p class="text-white font-bold flex items-center gap-2">
|
||||
<%= icone :parcial, cor: 'text-amber-400', espaco: false %>
|
||||
Notas fora da operação
|
||||
</p>
|
||||
<p class="text-xs text-gray-500 mt-0.5">
|
||||
Entregues em <%= inicio.strftime('%d/%m/%Y') %> – <%= fim.strftime('%d/%m/%Y') %>
|
||||
sem constar em nenhuma planilha <code class="text-gray-400">gade_entregas_*</code> — planos avulsos e de inclusão.
|
||||
</p>
|
||||
</div>
|
||||
<div class="flex items-center gap-4 text-sm">
|
||||
<span class="text-gray-400">NFs <strong class="text-white text-lg"><%= fora.total %></strong></span>
|
||||
<span class="text-gray-400">entregues <strong class="text-green-400 text-lg"><%= fora.entregues %></strong></span>
|
||||
<span class="text-gray-400">recusas <strong class="text-blue-400 text-lg"><%= fora.nao_entregues %></strong></span>
|
||||
<% if fora.pendentes.positive? %>
|
||||
<span class="text-gray-400">pendentes <strong class="text-yellow-400 text-lg"><%= fora.pendentes %></strong></span>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<% if fora.total.zero? %>
|
||||
<p class="px-6 py-8 text-center text-gray-500 text-sm">
|
||||
Nenhuma nota fora das planilhas de operação neste período.
|
||||
</p>
|
||||
<% else %>
|
||||
<%# Quebra por plano de origem — é aqui que "(Avulsa)" e "INCLUSÃO" se separam. %>
|
||||
<div class="flex flex-wrap gap-2 px-6 py-3 border-b border-white/5">
|
||||
<% fora.por_plano.first(8).each do |grupo| %>
|
||||
<span class="inline-flex items-center gap-2 px-3 py-1 rounded-full bg-white/5 border border-white/10 text-xs text-gray-300">
|
||||
<%= grupo[:nome] %> <strong class="text-white"><%= grupo[:total] %></strong>
|
||||
</span>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<div class="overflow-x-auto">
|
||||
<table class="w-full text-sm">
|
||||
<thead class="bg-white/5 text-gray-400 text-xs uppercase tracking-wider">
|
||||
<tr>
|
||||
<th class="text-left px-6 py-3 font-medium">NF</th>
|
||||
<th class="text-left px-4 py-3 font-medium">Plano / título</th>
|
||||
<th class="text-left px-4 py-3 font-medium">Motorista</th>
|
||||
<th class="text-left px-4 py-3 font-medium">Unidade</th>
|
||||
<th class="text-left px-4 py-3 font-medium">Data</th>
|
||||
<th class="text-left px-4 py-3 font-medium">Resultado</th>
|
||||
<th class="text-left px-4 py-3 font-medium">Motivo</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="divide-y divide-white/5">
|
||||
<% fora.listagem.each do |linha| %>
|
||||
<% sucesso = linha['status'] == 'completed' %>
|
||||
<tr class="hover:bg-white/5">
|
||||
<td class="px-6 py-2.5 text-white font-mono"><%= linha['reference_id'] %></td>
|
||||
<td class="px-4 py-2.5 text-gray-300 max-w-[22rem] truncate" title="<%= fora.plano(linha) %>"><%= fora.plano(linha) %></td>
|
||||
<td class="px-4 py-2.5 text-gray-300"><%= linha['driver'].presence || '—' %></td>
|
||||
<td class="px-4 py-2.5 text-gray-300"><%= linha['contact_name'].presence || '—' %></td>
|
||||
<td class="px-4 py-2.5 text-gray-400 whitespace-nowrap">
|
||||
<%= (linha['checkout'].presence || linha['planned_date'].presence)&.to_date&.strftime('%d/%m/%Y') || '—' %>
|
||||
</td>
|
||||
<td class="px-4 py-2.5">
|
||||
<span class="<%= sucesso ? 'text-green-400' : 'text-blue-400' %>"><%= sucesso ? 'Entregue' : (linha['status'].presence || '—') %></span>
|
||||
</td>
|
||||
<td class="px-4 py-2.5 text-gray-400"><%= linha['observation'].presence || '—' %></td>
|
||||
</tr>
|
||||
<% end %>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<% if fora.truncada? %>
|
||||
<p class="px-6 py-3 text-xs text-gray-500 border-t border-white/5">
|
||||
Mostrando as <%= Analytics::NotasForaOperacao::LIMITE %> mais recentes de <%= fora.total %>.
|
||||
</p>
|
||||
<% end %>
|
||||
<% end %>
|
||||
</div>
|
||||
@@ -24,6 +24,7 @@
|
||||
<div class="group bg-[#1a1a1a] rounded-2xl border border-white/5 p-7 transition-all duration-200 hover:border-white/20 hover:bg-[#202020] hover:-translate-y-0.5 hover:shadow-lg hover:shadow-black/40">
|
||||
<p class="text-gray-400 text-sm font-medium mb-1">Total de Entregas</p>
|
||||
<p class="text-6xl font-black text-white leading-none inline-block origin-left transition-transform duration-200 group-hover:scale-110"><%= metricas.total %></p>
|
||||
<p class="text-xs text-gray-500 mt-2">notas fiscais da operação</p>
|
||||
<p class="text-xs text-gray-500 mt-2 h-0 overflow-hidden opacity-0 transition-all duration-200 group-hover:h-4 group-hover:opacity-100">
|
||||
<%= icone :sucesso, cor: 'text-green-500', tamanho: 'w-4 h-4' %> <%= metricas.sucesso %> · <%= icone :ponto, cor: 'text-blue-500', tamanho: 'w-4 h-4' %> <%= metricas.recusas %> · <%= icone :parcial, cor: 'text-amber-400', tamanho: 'w-4 h-4' %> <%= metricas.pendentes %>
|
||||
</p>
|
||||
@@ -49,6 +50,37 @@
|
||||
<%= pct_total.(metricas.pendentes) %>% do total
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<%# Camada operacional: os cards acima contam NOTAS (o que o cliente paga e
|
||||
o que confere nos documentos). Aqui ficam as IDAS ao local — que é o
|
||||
que o motorista recebe e o que o dashboard financeiro conta. Sem esta
|
||||
faixa, uma NF que só foi entregue na segunda tentativa aparecia como
|
||||
sucesso puro e o insucesso sumia da tela. %>
|
||||
<div class="bg-[#141414] rounded-2xl border border-white/5 p-5">
|
||||
<p class="text-gray-400 text-xs font-medium uppercase tracking-wider mb-3">Visitas ao local</p>
|
||||
<dl class="space-y-2 text-sm">
|
||||
<div class="flex items-baseline justify-between gap-2">
|
||||
<dt class="text-gray-400">Visitas realizadas</dt>
|
||||
<dd class="text-white font-bold"><%= metricas.total_visitas %></dd>
|
||||
</div>
|
||||
<div class="flex items-baseline justify-between gap-2">
|
||||
<dt class="text-gray-400">Retentativas</dt>
|
||||
<dd class="<%= metricas.retentativas.positive? ? 'text-amber-400' : 'text-gray-500' %> font-bold"><%= metricas.retentativas %></dd>
|
||||
</div>
|
||||
<div class="flex items-baseline justify-between gap-2">
|
||||
<dt class="text-gray-400">Insucessos (por visita)</dt>
|
||||
<dd class="text-blue-400 font-bold"><%= metricas.visitas_insucesso %></dd>
|
||||
</div>
|
||||
<div class="flex items-baseline justify-between gap-2">
|
||||
<dt class="text-gray-400">Entregues na 2ª ida ou mais</dt>
|
||||
<dd class="text-white font-bold"><%= metricas.notas_reentregues %></dd>
|
||||
</div>
|
||||
</dl>
|
||||
<p class="text-[11px] leading-snug text-gray-500 mt-3">
|
||||
O dashboard financeiro conta VISITAS (o motorista recebe por ida); esta tela conta NOTAS.
|
||||
A diferença são as <%= metricas.retentativas %> retentativas acima.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<%# Coluna direita — gráficos/tabelas ocupam o espaço ao lado dos cards. %>
|
||||
|
||||
@@ -57,6 +57,7 @@
|
||||
<%= icone :comparar %> Comparação entre operações (cada uma no seu próprio período)
|
||||
<% elsif @metricas&.data_inicio %>
|
||||
<%= @metricas.operacoes_label %> · <%= @metricas.data_inicio.strftime('%d/%m/%Y') %> – <%= @metricas.data_fim.strftime('%d/%m/%Y') %>
|
||||
<%= @periodo_explicito ? '(período filtrado)' : '(operação inteira)' %>
|
||||
<% elsif @operacao %>
|
||||
<%= Operacao.label(@operacao) %>
|
||||
<% end %>
|
||||
@@ -64,9 +65,11 @@
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<%# Filtro de período — só na visão Global (a faixa de data não se aplica a
|
||||
operação única, que usa o período natural dos próprios dados). %>
|
||||
<% if @modo == 'global' %>
|
||||
<%# Filtro de período — Global e Operação. No modo Operação ele só entra em
|
||||
vigor quando o operador ESCOLHE uma faixa (params inicio/fim): sem isso o
|
||||
recorte é a operação inteira, senão abrir uma operação de meses atrás
|
||||
cairia no mês corrente e mostraria zero. %>
|
||||
<% if %w[global operacao].include?(@modo) %>
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
<div class="relative">
|
||||
<span class="pointer-events-none absolute right-3 top-1/2 -translate-y-1/2"><%= icone :calendario, espaco: false %></span>
|
||||
@@ -77,12 +80,22 @@
|
||||
</div>
|
||||
<div class="flex flex-wrap items-center gap-1">
|
||||
<% atalhos.each do |label, (ini, fim)| %>
|
||||
<% ativo = @periodo_inicio == ini && @periodo_fim == fim %>
|
||||
<%# No Global a faixa SEMPRE vale (é o que define a visão); no modo
|
||||
Operação só quando escolhida — senão o atalho apareceria aceso
|
||||
sem estar sendo aplicado. %>
|
||||
<% ativo = (@periodo_explicito || @modo == 'global') && @periodo_inicio == ini && @periodo_fim == fim %>
|
||||
<%= link_to label,
|
||||
operacoes_dashboard_path(modo: 'global', inicio: ini.strftime('%Y-%m-%d'), fim: fim.strftime('%Y-%m-%d')),
|
||||
operacoes_dashboard_path(ctx_ops.merge(modo: @modo, inicio: ini.strftime('%Y-%m-%d'), fim: fim.strftime('%Y-%m-%d'))),
|
||||
data: { turbo: false },
|
||||
class: "px-3 py-2.5 rounded-xl text-sm whitespace-nowrap border #{ativo ? 'bg-orange-500 text-black border-orange-500 font-bold' : 'bg-[#1a1a1a] text-gray-300 border-white/10 hover:border-orange-500'}" %>
|
||||
<% end %>
|
||||
<%# Volta ao recorte natural da operação (sem faixa de datas na URL). %>
|
||||
<% if @modo == 'operacao' %>
|
||||
<%= link_to 'Operação inteira',
|
||||
operacoes_dashboard_path(ctx_ops.merge(modo: 'operacao')),
|
||||
data: { turbo: false },
|
||||
class: "px-3 py-2.5 rounded-xl text-sm whitespace-nowrap border #{@periodo_explicito ? 'bg-[#1a1a1a] text-gray-300 border-white/10 hover:border-orange-500' : 'bg-orange-500 text-black border-orange-500 font-bold'}" %>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
@@ -90,12 +103,16 @@
|
||||
|
||||
<%# ── Abas de modo + atalho para a planilha ─────────────── %>
|
||||
<% modos = { 'operacao' => [:operacao, 'Operação'], 'global' => [:global, 'Global'], 'comparar' => [:comparar, 'Comparar'] } %>
|
||||
<%# A faixa de datas só viaja entre as abas se o operador tiver ESCOLHIDO uma.
|
||||
Carregar o default (mês corrente) ao entrar em "Operação" faria uma operação
|
||||
de meses atrás abrir zerada. %>
|
||||
<% ctx_periodo = @periodo_explicito ? { inicio: ini_iso, fim: fim_iso } : {} %>
|
||||
<div class="flex flex-wrap items-center justify-between gap-2 border-b border-white/5 pb-3">
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
<% modos.each do |m, (ic, label)| %>
|
||||
<% ativo = @modo == m %>
|
||||
<%= link_to rotulo(ic, label, cor: (ativo ? nil : 'text-brand-laranja')),
|
||||
operacoes_dashboard_path(ctx_ops.merge(modo: m, inicio: ini_iso, fim: fim_iso)),
|
||||
operacoes_dashboard_path(ctx_ops.merge(ctx_periodo).merge(modo: m)),
|
||||
data: { turbo: false },
|
||||
class: "px-4 py-2 rounded-xl text-sm font-semibold whitespace-nowrap #{ativo ? 'bg-orange-500 text-black' : 'bg-[#1a1a1a] text-gray-300 border border-white/10 hover:border-orange-500'}" %>
|
||||
<% end %>
|
||||
@@ -157,6 +174,9 @@
|
||||
<%= render 'painel', metricas: @metricas, filtravel: true %>
|
||||
<%= render 'mapa', metricas: @metricas %>
|
||||
<% end %>
|
||||
<% if @fora_operacao %>
|
||||
<%= render 'fora_operacao', fora: @fora_operacao, inicio: @periodo_inicio, fim: @periodo_fim %>
|
||||
<% end %>
|
||||
|
||||
<% elsif @modo == 'global' %>
|
||||
<div class="bg-[#1a1a1a] rounded-2xl border border-white/5 p-4 text-sm text-gray-300">
|
||||
@@ -167,6 +187,9 @@
|
||||
<%= render 'painel', metricas: @metricas, filtravel: true %>
|
||||
<%= render 'mapa', metricas: @metricas %>
|
||||
<% end %>
|
||||
<% if @fora_operacao %>
|
||||
<%= render 'fora_operacao', fora: @fora_operacao, inicio: @periodo_inicio, fim: @periodo_fim %>
|
||||
<% end %>
|
||||
|
||||
<% else # comparar %>
|
||||
<form method="get" action="<%= operacoes_dashboard_path %>" data-turbo="false" class="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||
|
||||
Reference in New Issue
Block a user