Files
Reem-Notas/app/views/operacoes_dashboard/_painel.html.erb
2026-07-03 15:06:19 -03:00

126 lines
6.4 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<%# locals: metricas (Analytics::OperacaoMetricas), titulo (opcional),
filtravel (opcional, default false) — torna as tabelas clicáveis p/ cross-filter %>
<% titulo = local_assigns[:titulo] %>
<% filtravel = local_assigns.fetch(:filtravel, false) %>
<% ins = metricas.insucessos_pct %>
<% pd = metricas.por_dia %>
<div class="space-y-4">
<% if titulo.present? %>
<div class="flex items-center justify-between">
<h2 class="text-lg font-bold text-white"><%= titulo %></h2>
<span class="text-xs text-gray-500">
<%= metricas.total %> entregas<% if metricas.data_inicio %> · <%= metricas.data_inicio.strftime('%d/%m/%Y') %><%= metricas.data_fim.strftime('%d/%m/%Y') %><% end %>
</span>
</div>
<% end %>
<%# ── KPIs ─────────────────────────────────────────────── %>
<% pct_total = ->(parte) { metricas.total.zero? ? 0 : (parte * 100.0 / metricas.total).round(1) } %>
<%# Cards de status compactos (orientação vertical da img 13, mas estreitos):
largura fixa que não ocupa a linha toda; os que couberem ficam lado a lado
e o restante quebra para a linha de baixo. %>
<div class="flex flex-wrap gap-3">
<div class="w-full sm:w-[calc(50%-0.375rem)] lg:w-52 bg-[#1a1a1a] rounded-2xl border border-white/5 p-5">
<p class="text-gray-400 text-sm font-medium mb-1">Total de Entregas</p>
<p class="text-4xl font-black text-white leading-none"><%= metricas.total %></p>
</div>
<div class="w-full sm:w-[calc(50%-0.375rem)] lg:w-52 bg-[#1a1a1a] rounded-2xl border border-white/5 p-5">
<p class="text-gray-400 text-sm font-medium mb-1">Total de Sucesso</p>
<p class="text-4xl font-black text-green-400 leading-none"><%= metricas.sucesso %></p>
<p class="text-xs text-gray-500 mt-2"><%= pct_total.(metricas.sucesso) %>% do total</p>
</div>
<div class="w-full sm:w-[calc(50%-0.375rem)] lg:w-52 bg-[#1a1a1a] rounded-2xl border border-white/5 p-5">
<p class="text-gray-400 text-sm font-medium mb-1">Total de Recusas</p>
<p class="text-4xl font-black text-blue-500 leading-none"><%= metricas.recusas %></p>
<p class="text-xs text-gray-500 mt-2"><%= pct_total.(metricas.recusas) %>% do total</p>
</div>
<div class="w-full sm:w-[calc(50%-0.375rem)] lg:w-52 bg-[#1a1a1a] rounded-2xl border border-white/5 p-5">
<p class="text-gray-400 text-sm font-medium mb-1">Entregas Pendentes</p>
<p class="text-4xl font-black text-yellow-400 leading-none"><%= metricas.pendentes %></p>
<p class="text-xs text-gray-500 mt-2"><%= pct_total.(metricas.pendentes) %>% do total</p>
</div>
</div>
<%# ── Insucessos % + Índices de falha + Status ─────────── %>
<div class="grid grid-cols-1 lg:grid-cols-3 gap-4">
<div class="bg-[#1a1a1a] rounded-2xl border border-white/5 p-6">
<p class="text-gray-400 text-sm font-medium mb-4">Insucessos %</p>
<% if metricas.total.zero? %>
<p class="text-center py-8 text-gray-500 text-sm">Sem dados no período.</p>
<% else %>
<div class="relative h-56">
<canvas class="dash-donut"
data-completed="<%= ins[:completed] %>"
data-failed="<%= ins[:failed] %>"></canvas>
</div>
<div class="flex justify-center gap-4 mt-3 text-xs">
<span class="text-orange-400">● Completas <%= ins[:pct_completed] %>%</span>
<span class="text-blue-500">● Falhas <%= ins[:pct_failed] %>%</span>
</div>
<% end %>
</div>
<div class="bg-[#1a1a1a] rounded-2xl border border-white/5 p-6">
<p class="text-gray-400 text-sm font-medium mb-4">Índices de Falha</p>
<%= render 'tabela_simples',
cabecalhos: ['Observação', 'Total', '%'],
linhas: metricas.indices_falha.map { |h| [h[:observation], h[:total], "#{h[:pct]}%"] },
vazio: 'Nenhuma falha no período.',
filtro: (filtravel ? 'f_obs' : nil) %>
</div>
<div class="bg-[#1a1a1a] rounded-2xl border border-white/5 p-6">
<p class="text-gray-400 text-sm font-medium mb-4">Por Status</p>
<%= render 'tabela_simples',
cabecalhos: ['Status', 'Quantidade'],
linhas: metricas.por_status_gade.map { |h| [h[:status], h[:total]] },
vazio: 'Sem dados.',
total: metricas.total,
filtro: (filtravel ? 'f_status' : nil) %>
</div>
</div>
<%# ── Motoristas + STS ─────────────────────────────────── %>
<div class="grid grid-cols-1 lg:grid-cols-2 gap-4">
<div class="bg-[#1a1a1a] rounded-2xl border border-white/5 p-6">
<p class="text-gray-400 text-sm font-medium mb-4">Notas fiscais por Motorista</p>
<div class="max-h-80 overflow-y-auto pr-2">
<%= render 'tabela_simples',
cabecalhos: ['Motorista', 'Notas fiscais'],
linhas: metricas.por_motorista.map { |h| [h[:nome], h[:total]] },
vazio: 'Sem dados.',
total: metricas.total,
filtro: (filtravel ? 'f_driver' : nil) %>
</div>
</div>
<div class="bg-[#1a1a1a] rounded-2xl border border-white/5 p-6">
<p class="text-gray-400 text-sm font-medium mb-4">Entregas por STS (Unidade)</p>
<div class="max-h-80 overflow-y-auto pr-2">
<%= render 'tabela_simples',
cabecalhos: ['Unidade', 'NFs entregues'],
linhas: metricas.por_sts.map { |h| [h[:nome], h[:total]] },
vazio: 'Sem dados.',
total: metricas.sucesso,
filtro: (filtravel ? 'f_sts' : nil) %>
</div>
</div>
</div>
<%# ── Entregas por dia ─────────────────────────────────── %>
<div class="bg-[#1a1a1a] rounded-2xl border border-white/5 p-6">
<p class="text-gray-400 text-sm font-medium mb-4">Entregas por Dia</p>
<% if pd[:labels].empty? %>
<p class="text-center py-8 text-gray-500 text-sm">Sem dados no período.</p>
<% else %>
<div class="relative h-72">
<canvas class="dash-bars"
data-labels="<%= pd[:labels].to_json %>"
data-completed="<%= pd[:completed].to_json %>"
data-failed="<%= pd[:failed].to_json %>"></canvas>
</div>
<% end %>
</div>
</div>