132 lines
6.3 KiB
Plaintext
132 lines
6.3 KiB
Plaintext
<%# 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 %>
|
||
|
||
<%# ── Layout 2 colunas: cards empilhados (esquerda, estreito) + resto (direita) ── %>
|
||
<% pct_total = ->(parte) { metricas.total.zero? ? 0 : (parte * 100.0 / metricas.total).round(1) } %>
|
||
<div class="flex flex-col lg:flex-row gap-4">
|
||
|
||
<%# Coluna esquerda — 4 cards de status EMPILHADOS na vertical (orientação da img 13). %>
|
||
<div class="w-full lg:w-60 lg:shrink-0 space-y-3">
|
||
<div class="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="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="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="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>
|
||
|
||
<%# Coluna direita — gráficos/tabelas ocupam o espaço ao lado dos cards. %>
|
||
<div class="flex-1 min-w-0 space-y-4">
|
||
|
||
<%# ── Insucessos % + Índices de falha + Status ─────────── %>
|
||
<div class="grid grid-cols-1 lg:grid-cols-2 xl: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><%# fim coluna direita %>
|
||
</div><%# fim layout 2 colunas %>
|
||
</div>
|