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

231 lines
10 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/consolidacoes/new.html.erb %>
<% content_for :title, 'Nova Consolidação' %>
<div class="max-w-3xl mx-auto">
<h1 class="text-2xl font-bold text-white mb-6"> Nova Consolidação</h1>
<%= form_with model: @consolidacao, class: 'bg-[#1a1a1a] border border-[#2a2a2a] rounded-xl p-6 space-y-6' do |f| %>
<% if @consolidacao.errors.any? %>
<div class="bg-red-900/40 border border-red-600 rounded-lg p-4">
<p class="text-red-400 font-bold mb-1">⚠️ Corrija os erros:</p>
<ul class="text-red-300 text-sm list-disc list-inside">
<% @consolidacao.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<%# Operação — multi-select opcional (pré-preenche e filtra por NF) %>
<div>
<label class="block text-white font-semibold mb-2">
🗂️ Operação <span class="text-gray-500 font-normal text-sm">(opcional — preenche motoristas, veículos e período, e filtra as entregas por nota fiscal)</span>
</label>
<div id="operacoes-lista"
class="bg-[#0a0a0a] border border-[#2a2a2a] rounded-lg p-3 max-h-48 overflow-y-auto space-y-1">
<% if @operacoes.any? %>
<% @operacoes.each do |op| %>
<label class="flex items-center gap-2 text-white hover:bg-[#1a1a1a] rounded px-2 py-2 cursor-pointer text-sm min-h-[44px]">
<%= check_box_tag 'consolidacao[operacoes][]', op[:tabela], false,
class: 'chk-operacao rounded text-orange-500 bg-[#1a1a1a] border-[#2a2a2a]', id: nil %>
<%= op[:label] %>
</label>
<% end %>
<% else %>
<p class="text-gray-500 text-sm px-2 py-2">Nenhuma tabela de operação encontrada no banco.</p>
<% end %>
</div>
<p id="operacao-status" class="text-gray-500 text-xs mt-1"></p>
</div>
<%# Nome %>
<div>
<%= f.label :nome, 'Nome da consolidação', class: 'block text-white font-semibold mb-2' %>
<%= f.text_field :nome, placeholder: 'Ex: Pagamento Janeiro/2026 — Quinzena 1',
class: 'w-full bg-[#0a0a0a] border border-[#2a2a2a] text-white rounded-lg px-4 py-3 focus:border-orange-500 focus:outline-none text-base' %>
</div>
<%# Período %>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<%= f.label :data_inicio, 'Data início', class: 'block text-white font-semibold mb-2' %>
<%= f.date_field :data_inicio,
class: 'w-full bg-[#0a0a0a] border border-[#2a2a2a] text-white rounded-lg px-4 py-3 focus:border-orange-500 focus:outline-none' %>
</div>
<div>
<%= f.label :data_fim, 'Data fim', class: 'block text-white font-semibold mb-2' %>
<%= f.date_field :data_fim,
class: 'w-full bg-[#0a0a0a] border border-[#2a2a2a] text-white rounded-lg px-4 py-3 focus:border-orange-500 focus:outline-none' %>
</div>
</div>
<%# Motoristas — multi-select %>
<div>
<label class="block text-white font-semibold mb-2">
Motoristas <span class="text-orange-500">*</span>
<span class="text-gray-500 font-normal text-sm">(<%= @motoristas.size %> encontrados no banco)</span>
</label>
<div class="bg-[#0a0a0a] border border-[#2a2a2a] rounded-lg p-3 max-h-64 overflow-y-auto space-y-1">
<% if @motoristas.any? %>
<label class="flex items-center gap-2 text-orange-400 text-sm font-semibold pb-2 border-b border-[#2a2a2a] cursor-pointer">
<input type="checkbox" onclick="document.querySelectorAll('.chk-motorista').forEach(c => c.checked = this.checked)"
class="rounded text-orange-500 bg-[#1a1a1a] border-[#2a2a2a]">
Selecionar todos
</label>
<% @motoristas.each do |m| %>
<label class="flex items-center gap-2 text-white hover:bg-[#1a1a1a] rounded px-2 py-2 cursor-pointer min-h-[44px]">
<%= check_box_tag 'motoristas[]', m, false, class: 'chk-motorista rounded text-orange-500 bg-[#1a1a1a] border-[#2a2a2a]', id: nil %>
<%= m %>
</label>
<% end %>
<% else %>
<p class="text-gray-500 text-sm p-2">Nenhum motorista encontrado na tabela de entregas.</p>
<% end %>
</div>
</div>
<%# Veículos — multi-select opcional (filtrado pelo motorista + período) %>
<div>
<label class="block text-white font-semibold mb-2">
Veículos <span class="text-gray-500 font-normal text-sm">(opcional — só os veículos usados pelo(s) motorista(s) no período)</span>
</label>
<div id="veiculos-lista"
data-url="<%= veiculos_disponiveis_consolidacoes_path %>"
class="bg-[#0a0a0a] border border-[#2a2a2a] rounded-lg p-3 max-h-48 overflow-y-auto space-y-1">
<p class="text-gray-500 text-sm px-2 py-2">Selecione um motorista para listar os veículos.</p>
</div>
</div>
<%# Ações %>
<div class="flex gap-3 pt-2">
<%= f.submit 'Criar e iniciar validação →',
class: 'flex-1 bg-orange-500 hover:bg-orange-600 text-black font-bold py-3.5 rounded-lg min-h-[48px] cursor-pointer transition-colors text-base' %>
<%= link_to 'Cancelar', consolidacoes_path,
class: 'px-6 py-3.5 text-gray-400 hover:text-white border border-[#2a2a2a] rounded-lg min-h-[48px] flex items-center' %>
</div>
<% end %>
</div>
<script>
(function () {
const lista = document.getElementById('veiculos-lista');
if (!lista) return;
const url = lista.dataset.url;
const dataIni = document.getElementById('consolidacao_data_inicio');
const dataFim = document.getElementById('consolidacao_data_fim');
// Mantém os veículos que já estavam marcados ao recarregar a lista.
function veiculosMarcados() {
return Array.from(lista.querySelectorAll('input[name="consolidacao[vehicle_ids][]"]:checked'))
.map(c => c.value);
}
function motoristasSelecionados() {
return Array.from(document.querySelectorAll('.chk-motorista:checked')).map(c => c.value);
}
function escapeHtml(s) {
return String(s).replace(/[&<>"']/g, c =>
({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' }[c]));
}
function renderVeiculos(veiculos, marcados) {
if (!veiculos.length) {
lista.innerHTML = '<p class="text-gray-500 text-sm px-2 py-2">Nenhum veículo encontrado para este(s) motorista(s) no período.</p>';
return;
}
lista.innerHTML = veiculos.map(function (v) {
const checked = marcados.includes(v) ? 'checked' : '';
const val = escapeHtml(v);
return '<label class="flex items-center gap-2 text-white hover:bg-[#1a1a1a] rounded px-2 py-2 cursor-pointer text-sm min-h-[44px]">' +
'<input type="checkbox" name="consolidacao[vehicle_ids][]" value="' + val + '" ' + checked +
' class="rounded text-orange-500 bg-[#1a1a1a] border-[#2a2a2a]">' + val + '</label>';
}).join('');
}
let controller = null;
async function atualizarVeiculos() {
const motoristas = motoristasSelecionados();
const marcados = veiculosMarcados();
if (!motoristas.length) {
lista.innerHTML = '<p class="text-gray-500 text-sm px-2 py-2">Selecione um motorista para listar os veículos.</p>';
return;
}
const params = new URLSearchParams();
motoristas.forEach(m => params.append('motoristas[]', m));
if (dataIni && dataIni.value) params.append('inicio', dataIni.value);
if (dataFim && dataFim.value) params.append('fim', dataFim.value);
if (controller) controller.abort();
controller = new AbortController();
try {
const resp = await fetch(url + '?' + params.toString(), {
headers: { 'Accept': 'application/json' },
signal: controller.signal
});
const data = await resp.json();
renderVeiculos(data.veiculos || [], marcados);
} catch (e) {
if (e.name !== 'AbortError') {
lista.innerHTML = '<p class="text-red-400 text-sm px-2 py-2">Erro ao carregar veículos.</p>';
}
}
}
document.addEventListener('change', function (e) {
if (e.target.classList.contains('chk-motorista')) atualizarVeiculos();
});
if (dataIni) dataIni.addEventListener('change', atualizarVeiculos);
if (dataFim) dataFim.addEventListener('change', atualizarVeiculos);
// ── Operações: ao marcar, pré-preenche período + motoristas + veículos ──
const opStatus = document.getElementById('operacao-status');
const dadosOpUrl = '<%= dados_da_operacao_consolidacoes_path %>';
function operacoesSelecionadas() {
return Array.from(document.querySelectorAll('.chk-operacao:checked')).map(c => c.value);
}
async function aplicarOperacoes() {
const ops = operacoesSelecionadas();
if (!ops.length) { if (opStatus) opStatus.textContent = ''; return; }
if (opStatus) opStatus.textContent = 'Carregando dados da operação…';
const params = new URLSearchParams();
ops.forEach(o => params.append('operacoes[]', o));
try {
const resp = await fetch(dadosOpUrl + '?' + params.toString(), { headers: { 'Accept': 'application/json' } });
const data = await resp.json();
if (data.inicio && dataIni) dataIni.value = data.inicio;
if (data.fim && dataFim) dataFim.value = data.fim;
const motset = new Set(data.motoristas || []);
document.querySelectorAll('.chk-motorista').forEach(c => { if (motset.has(c.value)) c.checked = true; });
await atualizarVeiculos();
const vehset = new Set(data.veiculos || []);
lista.querySelectorAll('input[name="consolidacao[vehicle_ids][]"]').forEach(c => { if (vehset.has(c.value)) c.checked = true; });
if (opStatus) {
opStatus.textContent = (data.motoristas || []).length + ' motorista(s) e ' +
(data.veiculos || []).length + ' veículo(s) nesta(s) operação(ões).';
}
} catch (e) {
if (opStatus) opStatus.textContent = 'Erro ao carregar dados da operação.';
}
}
document.addEventListener('change', function (e) {
if (e.target.classList.contains('chk-operacao')) aplicarOperacoes();
});
// Estado inicial (caso o form volte com motoristas já marcados após erro de validação)
if (motoristasSelecionados().length) atualizarVeiculos();
})();
</script>