Atualização do função de editar lançamento para quer consiga listar todas
This commit is contained in:
@@ -4,6 +4,7 @@
|
||||
/history/ da API). Os dados do card chegam via fetch (endpoint buscar). %>
|
||||
<div class="space-y-6" id="edicao-app"
|
||||
data-buscar-url="<%= buscar_admin_edicao_lancamento_path %>"
|
||||
data-carregar-url="<%= carregar_admin_edicao_lancamento_path %>"
|
||||
data-atualizar-url="<%= atualizar_admin_edicao_lancamento_path %>"
|
||||
data-historico-url="<%= historico_admin_edicao_lancamento_path %>"
|
||||
data-csrf="<%= form_authenticity_token %>"
|
||||
@@ -43,6 +44,16 @@
|
||||
<p id="busca-loading" class="hidden mt-2 text-sm text-gray-400">Consultando SimpliRoute…</p>
|
||||
</div>
|
||||
|
||||
<%# Lançamentos da NF — só aparece quando há mais de um. Plano duplicado no
|
||||
SimpliRoute cria uma visita nova com a MESMA NF, e a antiga (a que tem o
|
||||
motorista) continua valendo: aqui o ADM vê as duas e escolhe qual editar. %>
|
||||
<div id="ocorrencias" class="hidden bg-[#111] border border-white/5 rounded-2xl p-5 space-y-3">
|
||||
<p class="text-sm text-gray-300">
|
||||
<span id="oc-contador" class="font-semibold text-white"></span> <span id="oc-dica"></span>
|
||||
</p>
|
||||
<div id="oc-lista" class="space-y-2"></div>
|
||||
</div>
|
||||
|
||||
<%# Card do lançamento (preenchido via JS após a busca) %>
|
||||
<div id="card" class="hidden bg-[#111] border border-white/5 rounded-2xl overflow-hidden">
|
||||
|
||||
@@ -142,13 +153,16 @@
|
||||
const app = document.getElementById('edicao-app');
|
||||
const URLS = {
|
||||
buscar: app.dataset.buscarUrl,
|
||||
carregar: app.dataset.carregarUrl,
|
||||
atualizar: app.dataset.atualizarUrl,
|
||||
historico: app.dataset.historicoUrl
|
||||
};
|
||||
const CSRF = app.dataset.csrf;
|
||||
const MOTIVOS = JSON.parse(app.dataset.motivos || '[]');
|
||||
|
||||
let visita = null; // estado atual do lançamento carregado
|
||||
let visita = null; // estado atual do lançamento carregado
|
||||
let ocorrencias = []; // todos os lançamentos da NF buscada
|
||||
let selecionado = null; // tracking_id do lançamento aberto no card
|
||||
|
||||
// ── Config dos campos editáveis ─────────────────────────────
|
||||
// Ícone do sprite (public/icons.svg). fill=currentColor -> herda a cor do badge.
|
||||
@@ -192,14 +206,19 @@
|
||||
|
||||
loadEl.classList.remove('hidden');
|
||||
cardEl.classList.add('hidden');
|
||||
ocorrencias = [];
|
||||
selecionado = null;
|
||||
try {
|
||||
const resp = await fetch(URLS.buscar + '?nf=' + encodeURIComponent(nf), { headers: { 'Accept': 'application/json' } });
|
||||
const data = await resp.json();
|
||||
if (!data.ok) { mostrarErroBusca(data.erro || 'Não foi possível localizar a entrega.'); return; }
|
||||
visita = data.visita;
|
||||
renderCard();
|
||||
cardEl.classList.remove('hidden');
|
||||
if (!data.ok) { renderOcorrencias(); mostrarErroBusca(data.erro || 'Não foi possível localizar a entrega.'); return; }
|
||||
ocorrencias = data.ocorrencias || [];
|
||||
// Só veio o card pronto quando a NF tem um lançamento único; com mais de
|
||||
// um, o ADM escolhe na lista antes de abrir qualquer coisa.
|
||||
if (data.visita) { abrirCard(data.visita); } else { visita = null; renderOcorrencias(); }
|
||||
if (!data.visita && !ocorrencias.length) mostrarErroBusca('Nenhum lançamento localizado para esta NF.');
|
||||
} catch (e) {
|
||||
renderOcorrencias();
|
||||
mostrarErroBusca('Erro de conexão ao buscar a NF.');
|
||||
} finally {
|
||||
loadEl.classList.add('hidden');
|
||||
@@ -210,6 +229,89 @@
|
||||
document.getElementById('btn-buscar').addEventListener('click', buscar);
|
||||
campoNf.addEventListener('keydown', e => { if (e.key === 'Enter') { e.preventDefault(); buscar(); } });
|
||||
|
||||
// ── Lista de lançamentos da NF ──────────────────────────────
|
||||
const ocEl = document.getElementById('ocorrencias');
|
||||
const ocLista = document.getElementById('oc-lista');
|
||||
const ocCont = document.getElementById('oc-contador');
|
||||
const ocDica = document.getElementById('oc-dica');
|
||||
|
||||
const fmtData = v => {
|
||||
if (!v) return '—';
|
||||
const d = new Date(v);
|
||||
return isNaN(d) ? String(v).slice(0, 10) : d.toLocaleDateString('pt-BR');
|
||||
};
|
||||
|
||||
// Com um lançamento editável só, a lista some — a tela fica igual ao que era
|
||||
// antes. Fica visível se ele não for editável, para explicar o porquê.
|
||||
function renderOcorrencias() {
|
||||
const n = ocorrencias.length;
|
||||
if (n === 0 || (n === 1 && ocorrencias[0].editavel)) {
|
||||
ocEl.classList.add('hidden'); ocLista.innerHTML = ''; return;
|
||||
}
|
||||
|
||||
ocCont.textContent = n + (n === 1 ? ' lançamento' : ' lançamentos');
|
||||
ocDica.textContent = n > 1 ? 'para esta NF — escolha qual quer editar.' : 'para esta NF.';
|
||||
ocLista.innerHTML = ocorrencias.map((o, i) => {
|
||||
const cfg = STATUS[o.status] || { label: o.status || '—', cls: 'bg-gray-800 text-gray-400', icone: 'question-circle' };
|
||||
const ativo = selecionado != null && String(selecionado) === String(o.tracking_id);
|
||||
return '' +
|
||||
'<div class="flex flex-wrap items-center justify-between gap-3 p-3 rounded-xl border ' +
|
||||
(ativo ? 'border-orange-500/60 bg-orange-500/10' : 'border-white/10 bg-[#1a1a1a]') + '">' +
|
||||
'<div class="min-w-0">' +
|
||||
'<div class="flex items-center gap-2 flex-wrap">' +
|
||||
'<span class="inline-flex items-center gap-1 px-2 py-0.5 rounded-full text-xs font-bold ' + cfg.cls + '">' +
|
||||
ico(cfg.icone) + ' ' + esc(cfg.label) + '</span>' +
|
||||
'<span class="text-white text-sm font-semibold">' + esc(fmtData(o.data)) + '</span>' +
|
||||
'<span class="text-xs text-gray-500">Visita ' + esc(o.id ?? '—') + '</span>' +
|
||||
'</div>' +
|
||||
'<p class="text-xs text-gray-400 mt-1">' +
|
||||
(o.motorista ? esc(o.motorista) : '<span class="text-gray-600">sem motorista</span>') + ' · ' +
|
||||
(o.veiculo ? esc(o.veiculo) : '<span class="text-gray-600">sem veículo</span>') +
|
||||
'</p>' +
|
||||
(o.aviso ? '<p class="text-xs text-amber-400 mt-1">' + esc(o.aviso) + '</p>' : '') +
|
||||
'</div>' +
|
||||
(o.editavel
|
||||
? '<button type="button" data-oc="' + i + '" class="px-4 py-2 text-sm font-semibold rounded-xl transition-colors ' +
|
||||
(ativo ? 'bg-white/5 text-gray-400 border border-white/10' : 'bg-[#f97316] hover:bg-orange-500 text-white') + '">' +
|
||||
(ativo ? 'Editando' : 'Editar') + '</button>'
|
||||
: '') +
|
||||
'</div>';
|
||||
}).join('');
|
||||
}
|
||||
|
||||
ocLista.addEventListener('click', e => {
|
||||
const btn = e.target.closest('[data-oc]');
|
||||
if (btn) abrirOcorrencia(ocorrencias[Number(btn.dataset.oc)]);
|
||||
});
|
||||
|
||||
async function abrirOcorrencia(o) {
|
||||
if (!o || !o.id) return;
|
||||
erroEl.classList.add('hidden');
|
||||
loadEl.classList.remove('hidden');
|
||||
cardEl.classList.add('hidden');
|
||||
try {
|
||||
const resp = await fetch(URLS.carregar + '?visit_id=' + encodeURIComponent(o.id) +
|
||||
(o.tracking_id ? '&tracking_id=' + encodeURIComponent(o.tracking_id) : ''),
|
||||
{ headers: { 'Accept': 'application/json' } });
|
||||
const data = await resp.json();
|
||||
if (!data.ok) { mostrarErroBusca(data.erro || 'Não foi possível abrir este lançamento.'); return; }
|
||||
abrirCard(data.visita);
|
||||
cardEl.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
||||
} catch (e) {
|
||||
mostrarErroBusca('Erro de conexão ao abrir o lançamento.');
|
||||
} finally {
|
||||
loadEl.classList.add('hidden');
|
||||
}
|
||||
}
|
||||
|
||||
function abrirCard(v) {
|
||||
visita = v;
|
||||
selecionado = v.tracking_id;
|
||||
renderCard();
|
||||
renderOcorrencias(); // destaca na lista qual está aberto
|
||||
cardEl.classList.remove('hidden');
|
||||
}
|
||||
|
||||
// ── Card ────────────────────────────────────────────────────
|
||||
function renderCard() {
|
||||
const v = visita;
|
||||
@@ -440,7 +542,12 @@
|
||||
if (data.campos && data.campos.length) Object.assign(visita, data.visita);
|
||||
pintarExibicao(div, def, data.campos && data.campos.length > 0);
|
||||
div.classList.add('cursor-pointer');
|
||||
if (def.campo === 'status') renderBadge();
|
||||
if (def.campo === 'status') {
|
||||
renderBadge();
|
||||
// Mantém a lista coerente com o que acabou de ser gravado.
|
||||
const o = ocorrencias.find(x => String(x.tracking_id) === String(visita.tracking_id));
|
||||
if (o) { o.status = visita.status; renderOcorrencias(); }
|
||||
}
|
||||
} catch (e) {
|
||||
msgErro.textContent = 'Erro de conexão ao salvar.';
|
||||
msgErro.classList.remove('hidden');
|
||||
|
||||
Reference in New Issue
Block a user