Files
Reem-Notas/app/views/consolidacao_entregas/validar.html.erb

385 lines
24 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.
<%# app/views/consolidacao_entregas/validar.html.erb — PASSO 2: Validar entregas %>
<% content_for :title, "Validar — #{@motorista}" %>
<div class="max-w-6xl mx-auto lg:h-[calc(100vh-4rem)] lg:flex lg:flex-col" data-controller="validacao"
data-validacao-url-value="<%= classificar_consolidacao_consolidacao_entregas_path(@consolidacao) %>"
data-validacao-massa-url-value="<%= classificar_em_massa_consolidacao_consolidacao_entregas_path(@consolidacao) %>"
data-validacao-motorista-value="<%= @motorista %>"
data-validacao-total-value="<%= @total_entregas %>"
data-validacao-veiculo-value="<%= @veiculo %>"
data-validacao-preco-extraordinaria-value="<%= @precos[:extraordinaria] %>">
<%# Cabeçalho + controles fixos no topo (no desktop); só a lista de NFs rola. %>
<div class="lg:shrink-0">
<%# Header %>
<div class="mb-6">
<%= link_to '← Voltar (Passo 1)', wizard_consolidacao_path(@consolidacao),
class: 'inline-flex items-center gap-1 bg-[#1a1a1a] hover:bg-[#2a2a2a] text-white border border-[#2a2a2a] hover:border-orange-500 font-semibold px-4 py-2.5 rounded-lg text-sm min-h-[44px]' %>
<div class="flex items-center justify-between mt-2">
<div>
<h1 class="text-2xl font-bold text-white">🚚 <%= @motorista %></h1>
<p class="text-gray-400 text-sm"><%= @consolidacao.nome %> · <%= l @consolidacao.data_inicio, format: :short %> → <%= l @consolidacao.data_fim, format: :short %></p>
</div>
<%= link_to 'Revisar (Passo 3) →',
revisar_consolidacao_consolidacao_entregas_path(@consolidacao, motorista: @motorista),
class: 'bg-orange-500 hover:bg-orange-600 text-black font-bold px-5 py-3 rounded-lg min-h-[48px] flex items-center' %>
</div>
</div>
<%# Barra de progresso (reflete o veículo selecionado, se houver) %>
<div class="bg-[#1a1a1a] border border-[#2a2a2a] rounded-xl p-4 mb-4">
<div class="flex justify-between text-sm mb-2">
<span class="text-white font-semibold">
<% if @veiculo.present? %>🚚 <%= @veiculo %> — <% end %>
<span data-validacao-target="contadorClassificadas"><%= @classificadas %></span>
de <span data-validacao-target="contadorTotal"><%= @total_entregas %></span> entregas classificadas
<span data-validacao-target="manuaisInfo" class="<%= 'hidden' unless @manuais.positive? %> text-blue-400 font-normal">·
+<span data-validacao-target="contadorManuais"><%= @manuais %></span> manuais</span>
</span>
<span class="text-orange-500 font-bold" data-validacao-target="percentual"></span>
</div>
<%# Barra com 2 segmentos: laranja/verde = elegíveis classificadas · azul = lançamentos manuais %>
<div class="w-full bg-[#0a0a0a] rounded-full h-3 flex overflow-hidden">
<div data-validacao-target="barra" class="h-3 bg-orange-500 transition-all duration-300" style="width: 0%"></div>
<div data-validacao-target="barraManuais" class="h-3 bg-blue-500 transition-all duration-300" style="width: 0%"></div>
</div>
</div>
<%# Fechar por veículo — filtra a tela por carro e mostra quais já estão fechados.
Só aparece quando o motorista usou mais de um veículo no período. %>
<% if @veiculos_status.size > 1 %>
<div class="bg-[#1a1a1a] border border-[#2a2a2a] rounded-xl p-4 mb-4">
<div class="flex items-center gap-2 mb-2">
<span class="text-gray-400 text-sm font-semibold">🚗 Fechar por veículo:</span>
<span class="text-gray-600 text-xs">clique para filtrar e classificar um carro de cada vez</span>
</div>
<div class="flex flex-wrap gap-2">
<%# Chip "Todos" %>
<%= link_to validar_consolidacao_consolidacao_entregas_path(@consolidacao, motorista: @motorista),
class: "px-3 py-2 rounded-lg text-sm font-semibold border min-h-[40px] flex items-center
#{@veiculo.blank? ? 'border-orange-500 text-orange-400 bg-orange-500/10' : 'border-[#2a2a2a] text-gray-300 hover:border-orange-500'}" do %>
Todos
<% end %>
<% @veiculos_status.each do |v| %>
<% selecionado = (@veiculo == v[:vehicle]) %>
<%= link_to validar_consolidacao_consolidacao_entregas_path(@consolidacao, motorista: @motorista, vehicle: v[:vehicle]),
data: { 'validacao-target': 'chipVeiculo', vehicle: v[:vehicle], eligible: v[:eligible] },
class: "px-3 py-2 rounded-lg text-sm font-semibold border min-h-[40px] flex items-center gap-2 transition-colors
#{v[:fechado] ? 'border-green-500 text-green-400 bg-green-500/10' : (selecionado ? 'border-orange-500 text-orange-400 bg-orange-500/10' : 'border-[#2a2a2a] text-gray-300 hover:border-orange-500')}" do %>
<span><%= v[:vehicle] %></span>
<span class="text-xs <%= v[:fechado] ? 'text-green-400' : 'text-gray-500' %>">
<span data-chip-feito><%= v[:classificadas] %></span>/<span data-chip-total><%= v[:eligible] %></span>
<span data-chip-check><%= '✓' if v[:fechado] %></span>
</span>
<% end %>
<% end %>
</div>
</div>
<% end %>
<%# Ações em massa + filtro %>
<div class="bg-[#1a1a1a] border border-[#2a2a2a] rounded-xl p-4 mb-4 flex flex-wrap items-center gap-3">
<label class="flex items-center gap-2 text-gray-300 text-sm cursor-pointer select-none">
<input type="checkbox" data-validacao-target="selecionarTodos"
data-action="change->validacao#selecionarTodos"
class="rounded text-orange-500 bg-[#0a0a0a] border-[#2a2a2a] w-5 h-5">
Selecionar todos
</label>
<div class="flex items-center gap-2 flex-wrap border-l border-[#2a2a2a] pl-3">
<span class="text-gray-500 text-sm">Aplicar nos selecionados <span class="text-gray-600">(clique de novo p/ remover)</span>:</span>
<button data-action="click->validacao#marcarSelecionados" data-tipo="entrega_normal"
class="bg-orange-500 text-black font-bold px-3 py-2 rounded-lg text-xs min-h-[44px]">Normal</button>
<button data-action="click->validacao#marcarSelecionados" data-tipo="retirada"
class="bg-orange-800 text-white font-bold px-3 py-2 rounded-lg text-xs min-h-[44px]">Retirada</button>
<button data-action="click->validacao#marcarSelecionados" data-tipo="bonus"
class="bg-white text-black font-bold px-3 py-2 rounded-lg text-xs border border-orange-500 min-h-[44px]">Bônus</button>
<button data-action="click->validacao#marcarSelecionados" data-tipo="desconto"
class="bg-black text-white font-bold px-3 py-2 rounded-lg text-xs border border-gray-700 min-h-[44px]">Desconto</button>
<button data-action="click->validacao#marcarSelecionados" data-tipo="extraordinaria"
class="bg-purple-600 text-white font-bold px-3 py-2 rounded-lg text-xs min-h-[44px]">Extra</button>
</div>
<div class="ml-auto">
<%= link_to params[:apenas_pendentes] == '1' ? 'Mostrar todas' : 'Apenas não classificadas',
validar_consolidacao_consolidacao_entregas_path(@consolidacao, motorista: @motorista, vehicle: @veiculo,
apenas_pendentes: params[:apenas_pendentes] == '1' ? nil : '1'),
class: 'text-orange-400 hover:text-orange-300 text-sm border border-orange-500/40 px-3 py-2 rounded-lg' %>
</div>
</div>
<%# Caixa de ferramentas — concentra os lançamentos avulsos (registro manual de
entrega e entrega de termo) num único botão com menu suspenso. Os modais e
controllers existentes são reaproveitados; só o gatilho mudou. %>
<% unless @consolidacao.finalizada? && current_user.operador? %>
<div class="relative mb-4"
data-controller="ferramentas apontamento-manual apontamento-termo"
data-apontamento-manual-apontar-manual-url-value="<%= apontar_manual_consolidacao_consolidacao_entregas_path(@consolidacao) %>"
data-apontamento-termo-apontar-termo-url-value="<%= apontar_termo_consolidacao_consolidacao_entregas_path(@consolidacao) %>"
data-apontamento-termo-motorista-value="<%= @motorista %>">
<%# Botão único + menu suspenso %>
<button type="button" data-action="ferramentas#toggle"
class="w-full md:w-auto inline-flex items-center justify-center gap-2 bg-[#1a1a1a] hover:bg-[#2a2a2a] text-white border border-dashed border-[#3a3a3a] hover:border-orange-500 font-semibold px-4 py-3 rounded-lg min-h-[48px]">
Adicionar lançamento
<span class="text-gray-500">▾</span>
</button>
<div data-ferramentas-target="menu"
class="hidden absolute z-40 mt-2 w-72 bg-[#1a1a1a] border border-[#2a2a2a] rounded-xl shadow-xl overflow-hidden">
<button type="button" data-action="apontamento-manual#abrir ferramentas#fechar"
class="w-full text-left px-4 py-3 hover:bg-[#2a2a2a] border-l-2 border-transparent hover:border-purple-500">
<span class="block text-white font-semibold">📝 Registrar entrega manualmente</span>
<span class="block text-gray-500 text-xs">fora da base de rastreio</span>
</button>
<button type="button" data-action="apontamento-termo#abrir ferramentas#fechar"
class="w-full text-left px-4 py-3 hover:bg-[#2a2a2a] border-t border-[#2a2a2a] border-l-2 border-l-transparent hover:border-l-blue-500">
<span class="block text-white font-semibold">📄 Entrega de termo</span>
<span class="block text-gray-500 text-xs">sem nota/código — só quantidade</span>
</button>
</div>
<%# Modal — registro manual de entrega %>
<div data-apontamento-manual-target="modal" data-action="click->apontamento-manual#fundo"
class="hidden fixed inset-0 z-50 bg-black/70 flex items-center justify-center p-4">
<div class="bg-[#1a1a1a] border border-[#2a2a2a] rounded-2xl w-full max-w-lg p-6 max-h-[90vh] overflow-y-auto">
<div class="flex items-center justify-between mb-1">
<h2 class="text-xl font-bold text-white">Registro manual de entrega</h2>
<button type="button" data-action="apontamento-manual#fechar"
class="text-gray-500 hover:text-white text-2xl leading-none">✕</button>
</div>
<p class="text-gray-400 text-sm mb-4">Lançe uma entrega à mão, sem buscar na base de rastreio. Use quando o motorista foi ao local mas a nota ficou registrada com outro motorista.</p>
<p data-apontamento-manual-target="erro" class="hidden bg-red-500/10 text-red-400 border border-red-500/30 rounded-lg px-3 py-2 text-sm mb-3"></p>
<%# Motorista (default = o motorista da tela; pode trocar) %>
<label class="block text-gray-400 text-sm mb-1">Motorista</label>
<input data-apontamento-manual-target="motorista" type="text" list="motoristas-consolidacao"
value="<%= @motorista %>" placeholder="Nome do motorista"
class="w-full bg-[#0a0a0a] border border-[#2a2a2a] text-white rounded-lg px-3 py-3 text-base focus:border-purple-500 focus:outline-none mb-3">
<datalist id="motoristas-consolidacao">
<% Array(@motoristas_consolidacao).each do |nome| %>
<option value="<%= nome %>"></option>
<% end %>
</datalist>
<%# NF + observação (texto livre) %>
<div class="grid grid-cols-1 md:grid-cols-2 gap-3 mb-3">
<div>
<label class="block text-gray-400 text-sm mb-1">NF <span class="text-gray-600">(opcional)</span></label>
<input data-apontamento-manual-target="nf" type="text" inputmode="numeric" placeholder="Número da nota"
class="w-full bg-[#0a0a0a] border border-[#2a2a2a] text-white rounded-lg px-3 py-3 text-base focus:border-purple-500 focus:outline-none">
</div>
<div>
<label class="block text-gray-400 text-sm mb-1">Observação / local <span class="text-gray-600">(opcional)</span></label>
<input data-apontamento-manual-target="obs" type="text" placeholder="Ex.: UBS Sacomã"
class="w-full bg-[#0a0a0a] border border-[#2a2a2a] text-white rounded-lg px-3 py-3 text-base focus:border-purple-500 focus:outline-none">
</div>
</div>
<%# Pilares (múltiplos) %>
<label class="block text-gray-400 text-sm mb-2">Classificar como <span class="text-gray-600">(pode marcar mais de uma)</span></label>
<div class="grid grid-cols-2 gap-2 mb-3">
<% [['entrega_normal', '🚚 Entrega Normal', Configuracao.preco_entrega],
['retirada', '📦 Retirada', Configuracao.preco_retirada],
['bonus', '⭐ Bônus', Configuracao.preco_bonus],
['desconto', '⚠️ Desconto', Configuracao.preco_desconto],
['extraordinaria', '✨ Entrega Extraordinária', Configuracao.preco_extraordinaria]].each do |tipo, label, preco| %>
<label class="flex items-center gap-2 bg-[#0a0a0a] border border-[#2a2a2a] rounded-lg px-3 py-3 cursor-pointer hover:border-purple-500 text-sm text-white">
<input type="checkbox" value="<%= tipo %>" data-apontamento-manual-target="tipoCheck"
<%= 'checked' if tipo == 'entrega_normal' %>
class="accent-purple-500">
<span><%= label %><br><span class="text-gray-500 text-xs"><%= moeda(preco) %></span></span>
</label>
<% end %>
</div>
<%# Valor customizado da Extraordinária (opcional) %>
<div class="mb-4">
<label class="block text-gray-400 text-sm mb-1">Valor da Extraordinária <span class="text-gray-600">(opcional — vazio usa o padrão)</span></label>
<input data-apontamento-manual-target="valorExtra" type="text" inputmode="decimal" placeholder="<%= moeda(Configuracao.preco_extraordinaria) %>"
class="w-full bg-[#0a0a0a] border border-[#2a2a2a] text-white rounded-lg px-3 py-3 text-base focus:border-purple-500 focus:outline-none">
</div>
<button type="button" data-action="apontamento-manual#adicionar"
class="w-full bg-green-600 hover:bg-green-500 text-white font-bold py-3.5 rounded-lg min-h-[48px]">
Adicionar apontamento ✓
</button>
</div>
</div>
<%# Modal — entrega de termo %>
<div data-apontamento-termo-target="modal" data-action="click->apontamento-termo#fundo"
class="hidden fixed inset-0 z-50 bg-black/70 flex items-center justify-center p-4">
<div class="bg-[#1a1a1a] border border-[#2a2a2a] rounded-2xl w-full max-w-md p-6">
<div class="flex items-center justify-between mb-1">
<h2 class="text-xl font-bold text-white">📄 Entrega de termo</h2>
<button type="button" data-action="apontamento-termo#fechar"
class="text-gray-500 hover:text-white text-2xl leading-none">✕</button>
</div>
<p class="text-gray-400 text-sm mb-4">Entrega sem nota fiscal nem código de rastreio. Informe quantos termos foram entregues.</p>
<p data-apontamento-termo-target="erro" class="hidden bg-red-500/10 text-red-400 border border-red-500/30 rounded-lg px-3 py-2 text-sm mb-3"></p>
<%# Motorista (contexto da tela) + preço unitário %>
<div class="flex items-center justify-between bg-[#0a0a0a] border border-[#2a2a2a] rounded-lg px-4 py-3 mb-4">
<div>
<p class="text-gray-500 text-xs">Motorista</p>
<p class="text-white font-semibold">🚚 <%= @motorista %></p>
</div>
<div class="text-right">
<p class="text-gray-500 text-xs">Preço por termo</p>
<p class="text-blue-400 font-semibold"><%= moeda(Configuracao.preco_termo) %></p>
</div>
</div>
<%# Stepper de quantidade %>
<label class="block text-gray-400 text-sm mb-2">Quantidade de termos</label>
<div class="flex items-center justify-center gap-3 mb-5">
<button type="button" data-action="apontamento-termo#menos"
class="w-12 h-12 rounded-lg bg-[#0a0a0a] border border-[#2a2a2a] hover:border-blue-500 text-white text-2xl font-bold leading-none"></button>
<input data-apontamento-termo-target="quantidade" type="text" inputmode="numeric" value="1" readonly
class="w-24 text-center bg-[#0a0a0a] border border-[#2a2a2a] text-white text-2xl font-bold rounded-lg py-2.5 focus:outline-none">
<button type="button" data-action="apontamento-termo#mais"
class="w-12 h-12 rounded-lg bg-[#0a0a0a] border border-[#2a2a2a] hover:border-blue-500 text-white text-2xl font-bold leading-none">+</button>
</div>
<button type="button" data-action="apontamento-termo#adicionar"
class="w-full bg-green-600 hover:bg-green-500 text-white font-bold py-3.5 rounded-lg min-h-[48px]">
Adicionar termos ✓
</button>
</div>
</div>
</div>
<% end %>
</div>
<%# Fim do cabeçalho fixo — abaixo só a lista de NFs rola. %>
<div class="grid grid-cols-1 lg:grid-cols-4 gap-4 lg:flex-1 lg:min-h-0">
<%# ── Lista de entregas (3 colunas) — área rolável ──── %>
<div class="lg:col-span-3 space-y-2 lg:overflow-y-auto lg:pr-1">
<% if @entregas.any? %>
<% @entregas.each do |e| %>
<% tipos_ativos = (@classificacoes[e.tracking_id] || []).map(&:tipo) %>
<div class="bg-[#1a1a1a] border <%= e.falhada? ? 'border-red-500/50' : 'border-[#2a2a2a]' %> rounded-xl p-4 flex flex-col md:flex-row md:items-center gap-3"
data-validacao-target="linha" data-tracking="<%= e.tracking_id %>"
data-vehicle="<%= e.vehicle.presence || '(sem veículo)' %>"
data-classificada="<%= tipos_ativos.any? ? '1' : '0' %>">
<%# Checkbox seleção em massa %>
<input type="checkbox" data-validacao-target="checkbox" value="<%= e.tracking_id %>"
class="rounded text-orange-500 bg-[#0a0a0a] border-[#2a2a2a] w-5 h-5">
<%# Dados da entrega %>
<div class="flex-1 min-w-0">
<p class="text-white font-semibold truncate">
NF <%= e.numero_nf %> · <%= e.local %>
<% if e.falhada? %>
<span class="ml-1 align-middle text-[10px] font-bold uppercase tracking-wide bg-red-500/20 text-red-400 border border-red-500/40 px-1.5 py-0.5 rounded">⚠️ Insucesso</span>
<% else %>
<span class="ml-1 align-middle text-[10px] font-bold uppercase tracking-wide bg-green-500/20 text-green-400 border border-green-500/40 px-1.5 py-0.5 rounded">✓ Entregue</span>
<% end %>
</p>
<p class="text-gray-400 text-xs truncate">📍 <%= e.address.presence || 'sem endereço' %></p>
<p class="text-gray-500 text-xs">
📅 <%= e.checkout&.strftime('%d/%m/%Y') || '—' %> · 🚚 <%= e.vehicle.presence || 'sem veículo' %>
<% if e.atraso_minutos > 0 %> · ⏱️ atraso <%= e.atraso_minutos %>min<% end %>
</p>
</div>
<%# Toggles coloridos — laranja=normal, laranja escuro=retirada, branco=bônus, preto=desconto %>
<div class="flex gap-1.5" data-validacao-target="toggles" data-tracking="<%= e.tracking_id %>">
<% [
['entrega_normal', 'Normal', 'bg-orange-500 text-black'],
['retirada', 'Retirada', 'bg-orange-800 text-white'],
['bonus', 'Bônus', 'bg-white text-black border border-orange-500'],
['desconto', 'Desc.', 'bg-black text-white border border-gray-700'],
['extraordinaria', 'Extra', 'bg-purple-600 text-white']
].each do |tipo, label, cores| %>
<button data-action="click->validacao#classificar"
data-tracking="<%= e.tracking_id %>" data-tipo="<%= tipo %>"
class="toggle-btn <%= cores %> font-bold px-3 py-2.5 rounded-lg text-xs min-h-[44px] transition-all
<%= tipos_ativos.include?(tipo) ? 'ring-2 ring-green-400 scale-105' : 'opacity-50 hover:opacity-100' %>">
<%= label %>
</button>
<% end %>
</div>
</div>
<% end %>
<% else %>
<div class="bg-[#1a1a1a] border border-[#2a2a2a] rounded-xl p-10 text-center">
<p class="text-4xl mb-3">🎉</p>
<p class="text-gray-400">Nenhuma entrega pendente neste filtro.</p>
</div>
<% end %>
</div>
<%# ── Resumo lateral (1 coluna) — rola junto se precisar ─────── %>
<div class="lg:col-span-1 lg:overflow-y-auto">
<div class="bg-[#1a1a1a] border border-[#2a2a2a] rounded-xl p-5 space-y-4">
<h3 class="text-white font-bold">📊 Resumo</h3>
<div class="space-y-2 text-sm">
<div class="flex justify-between items-center">
<span class="flex items-center gap-2"><span class="w-3 h-3 bg-orange-500 rounded-full"></span> Normal</span>
<span class="text-white font-bold" data-validacao-target="qtdNormal">0</span>
</div>
<div class="flex justify-between items-center">
<span class="flex items-center gap-2"><span class="w-3 h-3 bg-orange-800 rounded-full"></span> Retirada</span>
<span class="text-white font-bold" data-validacao-target="qtdRetirada">0</span>
</div>
<div class="flex justify-between items-center">
<span class="flex items-center gap-2"><span class="w-3 h-3 bg-white rounded-full border border-orange-500"></span> Bônus</span>
<span class="text-white font-bold" data-validacao-target="qtdBonus">0</span>
</div>
<div class="flex justify-between items-center">
<span class="flex items-center gap-2"><span class="w-3 h-3 bg-black border border-gray-600 rounded-full"></span> Desconto</span>
<span class="text-white font-bold" data-validacao-target="qtdDesconto">0</span>
</div>
<div class="flex justify-between items-center">
<span class="flex items-center gap-2"><span class="w-3 h-3 bg-purple-600 rounded-full"></span> Extraordinária</span>
<span class="text-white font-bold" data-validacao-target="qtdExtraordinaria">0</span>
</div>
<div class="flex justify-between items-center">
<span class="flex items-center gap-2"><span class="w-3 h-3 bg-blue-600 rounded-full"></span> Termo</span>
<span class="text-white font-bold" data-validacao-target="qtdTermo">0</span>
</div>
</div>
<div class="border-t border-[#2a2a2a] pt-4 flex justify-between items-center text-sm">
<span class="flex items-center gap-2 text-blue-400"><span class="w-3 h-3 bg-blue-500 rounded-full"></span> Apontamentos manuais</span>
<span class="text-blue-400 font-bold" data-validacao-target="qtdManuais"><%= @manuais %></span>
</div>
<div class="border-t border-[#2a2a2a] pt-4">
<p class="text-gray-500 text-xs mb-1">Total do motorista</p>
<p class="text-orange-500 font-black text-2xl" data-validacao-target="valorTotal">R$ 0,00</p>
</div>
<div class="border-t border-[#2a2a2a] pt-4 text-xs text-gray-500 space-y-1">
<p>💰 Normal: <%= moeda(@precos[:entrega_normal]) %></p>
<p>📦 Retirada: <%= moeda(@precos[:retirada]) %></p>
<p>⭐ Bônus: <%= moeda(@precos[:bonus]) %></p>
<p>⚠️ Desconto: -<%= moeda(@precos[:desconto]) %></p>
<p>✨ Extraordinária: <%= moeda(@precos[:extraordinaria]) %> (valor por entrega)</p>
<p>📄 Termo: <%= moeda(@precos[:termo]) %></p>
</div>
</div>
</div>
</div>
</div>
<%# Estado inicial para o Stimulus %>
<script type="application/json" id="validacao-estado-inicial">
<% _todas = @classificacoes.values.flatten %>
<%= raw({
classificadas: @classificadas,
manuais: @manuais,
por_tipo: _todas.group_by(&:tipo).transform_values { |arr| arr.sum(&:quantidade) },
valor_total: _todas.sum { |c| c.tipo == 'desconto' ? -c.valor_aplicado : c.valor_aplicado }.to_f
}.to_json) %>
</script>