Files
Reem-Notas/app/views/consolidacoes/wizard.html.erb

303 lines
19 KiB
Plaintext

<%# app/views/consolidacoes/wizard.html.erb — PASSO 1: Selecionar motorista %>
<% content_for :title, "Wizard — #{@consolidacao.nome}" %>
<div class="max-w-3xl mx-auto">
<%# Header do wizard %>
<div class="mb-6">
<%= link_to '← Voltar para o resumo', consolidacao_path(@consolidacao), class: 'text-gray-500 hover:text-orange-500 text-sm' %>
<h1 class="text-2xl font-bold text-white mt-2"><%= @consolidacao.nome %></h1>
<p class="text-gray-400 text-sm">
<%= icone :calendario %> <%= l @consolidacao.data_inicio, format: :short %> <%= icone :avancar, tamanho: 'w-3.5 h-3.5' %> <%= l @consolidacao.data_fim, format: :short %>
· <%= badge_status(@consolidacao.status) %>
</p>
</div>
<%# Indicador de passos %>
<div class="flex items-center gap-2 mb-8">
<div class="flex items-center gap-2 text-orange-500 font-bold">
<span class="w-8 h-8 bg-orange-500 text-black rounded-full flex items-center justify-center">1</span>
Selecionar motorista
</div>
<div class="flex-1 h-px bg-[#2a2a2a]"></div>
<div class="flex items-center gap-2 text-gray-600">
<span class="w-8 h-8 bg-[#1a1a1a] border border-[#2a2a2a] rounded-full flex items-center justify-center">2</span>
Validar entregas
</div>
<div class="flex-1 h-px bg-[#2a2a2a]"></div>
<div class="flex items-center gap-2 text-gray-600">
<span class="w-8 h-8 bg-[#1a1a1a] border border-[#2a2a2a] rounded-full flex items-center justify-center">3</span>
Revisar
</div>
</div>
<%# Caixa de ferramentas do Passo 1 — concentra "Apontamento manual" (busca pela
NF) e "Adicionar motorista" num único botão com menu suspenso. %>
<% unless @consolidacao.finalizada? && current_user.operador? %>
<div class="relative mb-4"
data-controller="ferramentas apontamento adicionar-motorista"
data-apontamento-buscar-url-value="<%= buscar_nf_consolidacao_consolidacao_entregas_path(@consolidacao) %>"
data-apontamento-apontar-url-value="<%= apontar_consolidacao_consolidacao_entregas_path(@consolidacao) %>">
<%# 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]">
<%= icone :adicionar %> Adicionar / Ferramentas
<%= icone :expandir, cor: 'text-gray-500', espaco: false %>
</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#abrir ferramentas#fechar"
class="w-full text-left px-4 py-3 hover:bg-[#2a2a2a] border-l-2 border-transparent hover:border-orange-500">
<span class="block text-white font-semibold"><%= icone :apontamento %> Apontamento manual</span>
<span class="block text-gray-500 text-xs">nota que chegou por fora (busca pela NF)</span>
</button>
<button type="button" data-action="adicionar-motorista#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-green-500">
<span class="block text-white font-semibold"><%= icone :motorista %> Adicionar motorista</span>
<span class="block text-gray-500 text-xs">incluir/restaurar um motorista nesta consolidação</span>
</button>
</div>
<%# Modal — apontamento manual (busca NF) %>
<div data-apontamento-target="modal" data-action="click->apontamento#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 manual</h2>
<button type="button" data-action="apontamento#fechar"
class="text-gray-500 hover:text-white leading-none" aria-label="Fechar"><%= icone :fechar, cor: nil, tamanho: 'w-6 h-6', espaco: false %></button>
</div>
<p class="text-gray-400 text-sm mb-4">Busque a nota pela base de rastreio e adicione ao fechamento do motorista correspondente.</p>
<p data-apontamento-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>
<%# Busca da NF %>
<form data-action="submit->apontamento#buscar" class="flex gap-2 mb-4">
<input data-apontamento-target="nf" type="text" inputmode="numeric"
placeholder="Número da nota (NF)"
class="flex-1 bg-[#0a0a0a] border border-[#2a2a2a] text-white rounded-lg px-3 py-3 text-base focus:border-orange-500 focus:outline-none">
<button type="submit"
class="bg-orange-500 hover:bg-orange-600 text-black font-bold px-5 rounded-lg min-h-[48px]">Buscar</button>
</form>
<%# Resultados (preenchidos via JS) %>
<div data-apontamento-target="resultados" class="space-y-2 mb-4"></div>
<%# Confirmação — pilares (múltiplos) + adicionar (oculto até a busca retornar) %>
<div data-apontamento-target="confirmar" class="hidden border-t border-[#2a2a2a] pt-4">
<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-4">
<% [['entrega_normal', :caminhao, 'Entrega Normal', Configuracao.preco_entrega],
['retirada', :retirada, 'Retirada', Configuracao.preco_retirada],
['bonus', :bonus, 'Bônus', Configuracao.preco_bonus],
['desconto', :desconto, 'Desconto', Configuracao.preco_desconto],
['extraordinaria', :extraordinaria, 'Entrega Extraordinária', Configuracao.preco_extraordinaria]].each do |tipo, ic, label, preco| %>
<label class="flex items-center gap-2 bg-[#0a0a0a] border border-[#2a2a2a] rounded-lg px-3 py-3 cursor-pointer hover:border-orange-500 text-sm text-white">
<input type="checkbox" value="<%= tipo %>" data-apontamento-target="tipoCheck"
<%= 'checked' if tipo == 'entrega_normal' %>
class="accent-orange-500">
<span><%= icone ic %> <%= label %><br><span class="text-gray-500 text-xs"><%= moeda(preco) %></span></span>
</label>
<% end %>
</div>
<button type="button" data-action="apontamento#apontar"
class="w-full bg-green-600 hover:bg-green-500 text-white font-bold py-3.5 rounded-lg min-h-[48px]">
Adicionar apontamento <%= icone :confirmar, cor: nil, espaco: false %>
</button>
</div>
</div>
</div>
<%# Modal — adicionar motorista %>
<div data-adicionar-motorista-target="modal" data-action="click->adicionar-motorista#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 flex items-center gap-2"><%= icone :motorista, espaco: false %> Adicionar motorista</h2>
<button type="button" data-action="adicionar-motorista#fechar"
class="text-gray-500 hover:text-white leading-none" aria-label="Fechar"><%= icone :fechar, cor: nil, tamanho: 'w-6 h-6', espaco: false %></button>
</div>
<p class="text-gray-400 text-sm mb-4">Inclua um motorista nesta consolidação. Se ele estava arquivado, será restaurado.</p>
<%= form_with url: adicionar_motorista_consolidacao_path(@consolidacao), method: :post do |f| %>
<label class="block text-gray-400 text-sm mb-1">Motorista <span class="text-gray-600">(previstos no período)</span></label>
<input data-adicionar-motorista-target="input" type="text" name="motorista_nome"
list="motoristas-disponiveis" placeholder="Nome do motorista" autocomplete="off"
class="w-full bg-[#0a0a0a] border border-[#2a2a2a] text-white rounded-lg px-3 py-3 text-base focus:border-orange-500 focus:outline-none mb-4">
<datalist id="motoristas-disponiveis">
<% Array(@motoristas_periodo).each do |m| %>
<option value="<%= m[:nome] %>" label="<%= m[:veiculos].join(', ') if m[:veiculos].any? %>"></option>
<% end %>
</datalist>
<%# Lista de previstos no período (nome + veículos usados) — clique para preencher %>
<% if Array(@motoristas_periodo).any? %>
<p class="text-gray-500 text-xs mb-1">Previstos neste período (clique para escolher):</p>
<div class="max-h-48 overflow-y-auto space-y-1 mb-4 pr-1">
<% @motoristas_periodo.each do |m| %>
<button type="button" data-action="adicionar-motorista#selecionar" data-nome="<%= m[:nome] %>"
class="w-full text-left px-3 py-2 bg-[#0a0a0a] border border-[#2a2a2a] hover:border-orange-500 rounded-lg">
<span class="block text-white text-sm font-semibold"><%= m[:nome] %></span>
<% if m[:veiculos].any? %>
<span class="block text-gray-500 text-xs"><%= icone :caminhao %> <%= m[:veiculos].join(', ') %></span>
<% end %>
</button>
<% end %>
</div>
<% else %>
<p class="text-gray-500 text-xs mb-4">Nenhum motorista previsto no período — você ainda pode digitar um nome manualmente.</p>
<% end %>
<button type="submit"
class="w-full bg-green-600 hover:bg-green-500 text-white font-bold py-3.5 rounded-lg min-h-[48px]">
Adicionar <%= icone :confirmar, cor: nil, espaco: false %>
</button>
<% end %>
</div>
</div>
</div>
<% end %>
<%# Lista de motoristas %>
<% pode_arquivar_motorista = !(@consolidacao.finalizada? && current_user.operador?) %>
<div class="space-y-3">
<% @motoristas.each do |m| %>
<%# Container relativo: o card é um link; o botão de arquivar fica FORA da
âncora (não se pode aninhar <form>/<button_to> dentro de <a>). %>
<div class="relative">
<%= link_to validar_consolidacao_consolidacao_entregas_path(@consolidacao, motorista: m[:registro].motorista_nome),
class: "block bg-[#1a1a1a] border rounded-xl p-5 transition-colors
#{m[:completo] ? 'border-green-600 hover:border-green-500' : 'border-[#2a2a2a] hover:border-orange-500'}" do %>
<div class="flex items-center justify-between">
<div class="flex items-center gap-4">
<div class="w-12 h-12 rounded-full flex items-center justify-center text-xl font-black
<%= m[:completo] ? 'bg-green-600 text-white' : 'bg-orange-500 text-black' %>">
<%= m[:completo] ? icone(:confirmar, cor: nil, tamanho: 'w-7 h-7') : m[:registro].motorista_nome.first.upcase %>
</div>
<div>
<p class="text-white font-bold text-lg"><%= m[:registro].motorista_nome %></p>
<p class="text-gray-400 text-sm">
<%= m[:total] %> entregas pagas no período
· <span class="<%= m[:completo] ? 'text-green-400' : 'text-orange-400' %>">
<%= m[:classificadas] %>/<%= m[:total] %> classificadas
</span>
<% if m[:apontamentos].positive? %>
· <span class="text-orange-400"><%= icone :adicionar, cor: nil, tamanho: 'w-3.5 h-3.5' %> <%= m[:apontamentos] %> apontamento<%= 's' if m[:apontamentos] > 1 %></span>
<% end %>
</p>
<% if m[:veiculos_total] > 0 %>
<p class="text-xs mt-0.5">
<span class="<%= m[:veiculos_fechados] == m[:veiculos_total] ? 'text-green-400' : 'text-gray-400' %>">
<%= icone :veiculo, cor: nil %> <%= m[:veiculos_fechados] %>/<%= m[:veiculos_total] %> veículo<%= 's' if m[:veiculos_total] > 1 %> consolidado<%= 's' if m[:veiculos_fechados] != 1 %>
</span>
<% if m[:veiculos_pendentes].any? %>
<span class="text-orange-400">· pendente<%= 's' if m[:veiculos_pendentes].size > 1 %>: <%= m[:veiculos_pendentes].join(', ') %></span>
<% end %>
</p>
<% end %>
</div>
</div>
<div class="text-right <%= 'pr-10' if pode_arquivar_motorista %>">
<p class="text-orange-500 font-bold"><%= moeda(m[:registro].valor_total) %></p>
<%= icone :avancar, cor: 'text-gray-600', tamanho: 'w-6 h-6', espaco: false %>
</div>
</div>
<div class="mt-3">
<% pct = m[:total].zero? ? 0 : (m[:classificadas] * 100 / m[:total]) %>
<div class="w-full bg-[#0a0a0a] rounded-full h-2">
<div class="h-2 rounded-full <%= m[:completo] ? 'bg-green-500' : 'bg-orange-500' %> transition-all"
style="width: <%= pct %>%"></div>
</div>
</div>
<% end %>
<% if pode_arquivar_motorista %>
<%= button_to arquivar_motorista_consolidacao_path(@consolidacao, consolidacao_motorista_id: m[:registro].id),
method: :post,
form: { data: { turbo_confirm: "Arquivar #{m[:registro].motorista_nome} desta consolidação? Você poderá restaurar depois." },
class: 'absolute top-3 right-3' },
class: 'text-gray-500 hover:text-red-400 border border-[#2a2a2a] hover:border-red-500/50 rounded-lg w-9 h-9 flex items-center justify-center',
title: "Arquivar #{m[:registro].motorista_nome}" do %>
<%= icone :arquivar, cor: nil, tamanho: 'w-5 h-5', espaco: false %>
<% end %>
<% end %>
</div>
<% end %>
</div>
<%# Motoristas arquivados — restauráveis %>
<% if @arquivados.present? %>
<div class="mt-6">
<h2 class="text-gray-400 font-semibold text-sm mb-2 flex items-center gap-2"><%= icone :arquivar, espaco: false %> Motoristas arquivados (<%= @arquivados.size %>)</h2>
<div class="space-y-2">
<% @arquivados.each do |cm| %>
<div class="flex items-center justify-between bg-[#141414] border border-[#2a2a2a] border-dashed rounded-xl px-4 py-3">
<div>
<p class="text-gray-300 font-semibold"><%= cm.motorista_nome %></p>
<p class="text-gray-600 text-xs">Arquivado · não conta no total</p>
</div>
<div class="flex items-center gap-2">
<% if pode_arquivar_motorista %>
<%= button_to desarquivar_motorista_consolidacao_path(@consolidacao, consolidacao_motorista_id: cm.id),
method: :post,
class: 'text-green-400 hover:text-green-300 border border-[#2a2a2a] hover:border-green-500/50 px-3 py-2 rounded-lg text-xs min-h-[40px] inline-flex items-center gap-1.5' do %>
<%= icone :restaurar, cor: nil, tamanho: 'w-4 h-4' %> Restaurar
<% end %>
<% end %>
<% if policy(@consolidacao).destroy? %>
<%= button_to excluir_motorista_consolidacao_path(@consolidacao, consolidacao_motorista_id: cm.id),
method: :delete,
form: { data: { turbo_confirm: "Excluir DEFINITIVAMENTE #{cm.motorista_nome} desta consolidação? Os lançamentos dele serão apagados e isso não pode ser desfeito." } },
class: 'text-red-400 hover:text-red-300 border border-[#2a2a2a] hover:border-red-500/50 px-3 py-2 rounded-lg text-xs min-h-[40px] inline-flex items-center gap-1.5' do %>
<%= icone :excluir, cor: nil, tamanho: 'w-4 h-4' %> Excluir
<% end %>
<% end %>
</div>
</div>
<% end %>
</div>
</div>
<% end %>
<%# Finalizar — habilita quando tudo classificado. Se sobram veículos pendentes
mas já há veículos consolidados, oferece "arquivar pendentes e finalizar". %>
<% tudo_completo = @motoristas.all? { |m| m[:completo] } && @motoristas.any? %>
<% tem_consolidados = @motoristas.any? { |m| m[:veiculos_fechados] > 0 || m[:veiculos_prontos] > 0 } %>
<% tem_pendentes = @motoristas.any? { |m| m[:veiculos_pendentes].any? } %>
<div class="mt-8 bg-[#1a1a1a] border border-[#2a2a2a] rounded-xl p-5">
<div class="flex items-center justify-between gap-3 flex-wrap">
<div>
<p class="text-white font-bold">Total da consolidação</p>
<p class="text-orange-500 font-black text-3xl"><%= moeda(@consolidacao.valor_total) %></p>
</div>
<% if @consolidacao.rascunho? %>
<div class="flex flex-col sm:flex-row gap-2">
<% if !tudo_completo && tem_consolidados && tem_pendentes %>
<%= button_to finalizar_parcial_consolidacao_path(@consolidacao),
method: :post,
form: { data: { turbo_confirm: 'Arquivar TODOS os veículos ainda não classificados 100% e finalizar? Eles ficam de fora desta consolidação (você resolve depois). Os veículos consolidados são pagos normalmente.' } },
class: 'bg-orange-500 hover:bg-orange-600 text-black font-bold px-5 py-3.5 rounded-lg min-h-[48px] flex items-center justify-center text-center gap-2' do %>
<%= icone :travado, cor: nil, tamanho: 'w-5 h-5' %> Arquivar pendentes e finalizar
<% end %>
<% end %>
<%= button_to finalizar_consolidacao_path(@consolidacao),
method: :post,
disabled: !tudo_completo,
form: { data: { turbo_confirm: 'Finalizar? Após finalizada, a consolidação não poderá ser editada por operadores.' } },
class: "font-bold px-6 py-3.5 rounded-lg min-h-[48px] transition-colors inline-flex items-center justify-center gap-2
#{tudo_completo ? 'bg-green-600 hover:bg-green-500 text-white cursor-pointer' : 'bg-[#2a2a2a] text-gray-600 cursor-not-allowed'}" do %>
Finalizar consolidação <%= icone :confirmar, cor: nil, espaco: false %>
<% end %>
</div>
<% end %>
</div>
</div>
<% unless tudo_completo %>
<p class="text-gray-500 text-sm mt-2 text-center">
<%= icone :alerta, cor: 'text-amber-400' %> Classifique todas as entregas de todos os motoristas para finalizar tudo<% if tem_consolidados && tem_pendentes %> — ou use <strong>“Arquivar pendentes e finalizar”</strong> para fechar só os veículos já consolidados.<% end %>
</p>
<% end %>
</div>