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

371 lines
23 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" 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] %>">
<%# 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>
<span class="text-orange-500 font-bold" data-validacao-target="percentual"></span>
</div>
<div class="w-full bg-[#0a0a0a] rounded-full h-3">
<div data-validacao-target="barra" class="h-3 rounded-full bg-orange-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">
<span class="text-gray-400 text-sm font-semibold">Ações em massa:</span>
<button data-action="click->validacao#marcarTodos" data-tipo="entrega_normal"
class="bg-orange-500 hover:bg-orange-600 text-black font-bold px-4 py-2.5 rounded-lg text-sm min-h-[44px]">
✓ Marcar todos como Normal
</button>
<label class="flex items-center gap-2 text-gray-300 text-sm cursor-pointer select-none border-l border-[#2a2a2a] pl-3">
<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>
<label class="flex items-center gap-2 text-sm cursor-pointer select-none border-l border-[#2a2a2a] pl-3">
<input type="checkbox" data-validacao-target="modoRemover" data-action="change->validacao#alternarModo"
class="rounded text-red-500 bg-[#0a0a0a] border-[#2a2a2a] w-5 h-5">
<span class="text-gray-300">Modo remover (desmarcar)</span>
</label>
<div class="flex items-center gap-2">
<span class="text-gray-500 text-sm" data-validacao-target="rotuloAcao">Selecionados como:</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>
<%# Apontamento 100% manual — entrega que NÃO está na base de rastreio
(ex.: motorista foi ao local mas a NF ficou com outro no rastreio). %>
<% unless @consolidacao.finalizada? && current_user.operador? %>
<div data-controller="apontamento-manual"
data-apontamento-manual-apontar-manual-url-value="<%= apontar_manual_consolidacao_consolidacao_entregas_path(@consolidacao) %>"
class="mb-4">
<button type="button" data-action="apontamento-manual#abrir"
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-purple-500/60 hover:border-purple-500 font-semibold px-4 py-3 rounded-lg min-h-[48px]">
Apontamento 100% manual
<span class="text-gray-500 text-xs font-normal hidden md:inline">(entrega fora da base de rastreio)</span>
</button>
<%# Modal %>
<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">Apontamento 100% manual</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>
</div>
<%# Entrega de termo — entrega SEM nota fiscal nem código; só motorista + quantidade. %>
<div data-controller="apontamento-termo"
data-apontamento-termo-apontar-termo-url-value="<%= apontar_termo_consolidacao_consolidacao_entregas_path(@consolidacao) %>"
data-apontamento-termo-motorista-value="<%= @motorista %>"
class="mb-4">
<button type="button" data-action="apontamento-termo#abrir"
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-blue-500/60 hover:border-blue-500 font-semibold px-4 py-3 rounded-lg min-h-[48px]">
Entrega de termo
<span class="text-gray-500 text-xs font-normal hidden md:inline">(sem nota/código — só quantidade)</span>
</button>
<%# Modal %>
<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="number" inputmode="numeric" min="1" 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 class="grid grid-cols-1 lg:grid-cols-4 gap-4">
<%# ── Lista de entregas (3 colunas) ─────────────────── %>
<div class="lg:col-span-3 space-y-2">
<% 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, sticky) ─────────────── %>
<div class="lg:col-span-1">
<div class="bg-[#1a1a1a] border border-[#2a2a2a] rounded-xl p-5 sticky top-4 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>
<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>
</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,
por_tipo: _todas.group_by(&:tipo).transform_values(&:size),
valor_total: _todas.sum { |c| c.tipo == 'desconto' ? -c.valor_aplicado : c.valor_aplicado }.to_f
}.to_json) %>
</script>