Adição do botão de entrega manual
This commit is contained in:
@@ -60,9 +60,10 @@
|
||||
</thead>
|
||||
<tbody class="divide-y divide-[#2a2a2a]">
|
||||
<% @classificacoes.each do |c| %>
|
||||
<% endereco_manual = [c.nf_manual.present? ? "NF #{c.nf_manual}" : nil, c.obs_manual.presence].compact.join(' · ') %>
|
||||
<tr>
|
||||
<td class="px-4 py-3 text-white text-xs"><%= c.entrega_original&.vehicle.presence || '—' %></td>
|
||||
<td class="px-4 py-3 text-gray-300 text-xs"><%= c.entrega_original&.address.presence || '—' %></td>
|
||||
<td class="px-4 py-3 text-white text-xs"><%= c.entrega_original&.vehicle.presence || (c.manual? ? 'manual' : '—') %></td>
|
||||
<td class="px-4 py-3 text-gray-300 text-xs"><%= c.entrega_original&.address.presence || endereco_manual.presence || '—' %></td>
|
||||
<td class="px-4 py-3">
|
||||
<span class="<%= c.tipo_cor[:bg] %> <%= c.tipo_cor[:text] %> px-2 py-1 rounded text-xs font-bold">
|
||||
<%= c.tipo_cor[:label] %>
|
||||
|
||||
@@ -81,6 +81,89 @@
|
||||
</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>
|
||||
<% end %>
|
||||
|
||||
<div class="grid grid-cols-1 lg:grid-cols-4 gap-4">
|
||||
|
||||
<%# ── Lista de entregas (3 colunas) ─────────────────── %>
|
||||
|
||||
@@ -160,11 +160,16 @@
|
||||
<tbody class="divide-y divide-[#2a2a2a]">
|
||||
<% @apontamentos.each do |(motorista, _tracking), pilares| %>
|
||||
<% entrega = pilares.first.entrega_original %>
|
||||
<% nf_exibida = entrega&.numero_nf || pilares.first.nf_manual %>
|
||||
<% obs_manual = pilares.first.obs_manual %>
|
||||
<% total = pilares.sum { |p| p.desconto? ? -p.valor_aplicado : p.valor_aplicado } %>
|
||||
<tr>
|
||||
<td class="px-4 py-3">
|
||||
<p class="text-white font-bold">NF <%= entrega&.numero_nf || '—' %></p>
|
||||
<p class="text-gray-400 text-xs"><%= motorista %><% if entrega&.vehicle.present? %> · <%= entrega.vehicle %><% end %></p>
|
||||
<p class="text-white font-bold">NF <%= nf_exibida.presence || '—' %></p>
|
||||
<p class="text-gray-400 text-xs">
|
||||
<%= motorista %><% if entrega&.vehicle.present? %> · <%= entrega.vehicle %><% end %>
|
||||
<% if entrega.nil? && obs_manual.present? %> · <%= obs_manual %><% end %>
|
||||
</p>
|
||||
</td>
|
||||
<td class="px-4 py-3">
|
||||
<div class="flex flex-wrap gap-1">
|
||||
@@ -179,7 +184,7 @@
|
||||
<%= button_to '✕ Remover',
|
||||
remover_apontamento_consolidacao_consolidacao_entregas_path(@consolidacao, tracking_id: pilares.first.tracking_id, motorista: motorista),
|
||||
method: :delete,
|
||||
form: { data: { turbo_confirm: "Remover o apontamento da NF #{entrega&.numero_nf} de #{motorista}?" } },
|
||||
form: { data: { turbo_confirm: "Remover o apontamento da NF #{nf_exibida.presence || '—'} de #{motorista}?" } },
|
||||
class: 'text-gray-400 hover:text-red-400 border border-[#2a2a2a] hover:border-red-500/50 px-3 py-2 rounded-lg text-xs min-h-[40px]' %>
|
||||
</td>
|
||||
<% end %>
|
||||
|
||||
@@ -96,46 +96,84 @@
|
||||
<% end %>
|
||||
|
||||
<%# Lista de motoristas %>
|
||||
<% pode_arquivar_motorista = !(@consolidacao.finalizada? && current_user.operador?) %>
|
||||
<div class="space-y-3">
|
||||
<% @motoristas.each do |m| %>
|
||||
<%= 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] ? '✓' : m[:registro].motorista_nome.first.upcase %>
|
||||
<%# 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] ? '✓' : 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">➕ <%= m[:apontamentos] %> apontamento<%= 's' if m[:apontamentos] > 1 %></span>
|
||||
<% end %>
|
||||
</p>
|
||||
</div>
|
||||
</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">➕ <%= m[:apontamentos] %> apontamento<%= 's' if m[:apontamentos] > 1 %></span>
|
||||
<% end %>
|
||||
</p>
|
||||
<div class="text-right <%= 'pr-10' if pode_arquivar_motorista %>">
|
||||
<p class="text-orange-500 font-bold"><%= moeda(m[:registro].valor_total) %></p>
|
||||
<span class="text-gray-600 text-2xl">→</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-right">
|
||||
<p class="text-orange-500 font-bold"><%= moeda(m[:registro].valor_total) %></p>
|
||||
<span class="text-gray-600 text-2xl">→</span>
|
||||
<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>
|
||||
</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 %>
|
||||
<% 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}" %>
|
||||
<% 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">🗄️ 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>
|
||||
<% if pode_arquivar_motorista %>
|
||||
<%= button_to '♻️ Restaurar',
|
||||
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]' %>
|
||||
<% end %>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<%# Finalizar — só habilita se tudo classificado %>
|
||||
<% tudo_completo = @motoristas.all? { |m| m[:completo] } && @motoristas.any? %>
|
||||
<div class="mt-8 bg-[#1a1a1a] border border-[#2a2a2a] rounded-xl p-5 flex items-center justify-between">
|
||||
|
||||
Reference in New Issue
Block a user