diff --git a/app/controllers/admin/edicao_lancamentos_controller.rb b/app/controllers/admin/edicao_lancamentos_controller.rb index dbb4568..fa70edc 100644 --- a/app/controllers/admin/edicao_lancamentos_controller.rb +++ b/app/controllers/admin/edicao_lancamentos_controller.rb @@ -48,7 +48,17 @@ class Admin::EdicaoLancamentosController < ApplicationController notes: visita['notes'], checkout_time: visita['checkout_time'], checkout_latitude: visita['checkout_latitude'], - checkout_longitude: visita['checkout_longitude'] + checkout_longitude: visita['checkout_longitude'], + planned_date: visita['planned_date'], + contato: visita['contact_name'], + telefone: visita['contact_phone'], + # Fotos do card: fachada vem do rastreio (mesma foto do mapa de operações); + # pictures/assinatura vêm da API. Só URLs http(s) chegam ao . + foto_fachada: url_imagem(entrega.try(:foto_da_fachada)), + pictures: Array(visita['pictures']).filter_map { |u| url_imagem(u) }, + signature: url_imagem(visita['signature']), + motorista: entrega.driver, + veiculo: entrega.vehicle }, motivos: motivos_seguros } @@ -59,20 +69,26 @@ class Admin::EdicaoLancamentosController < ApplicationController end # PATCH /admin/edicao_lancamento/atualizar + # HTML: fluxo de form clássico (fallback). JSON: salvar-por-campo do card + # (click-to-edit) — o fetch manda visit_id + só o campo editado. def atualizar authorize :edicao_lancamento id = params[:visit_id].to_s.strip - return redirect_to(admin_edicao_lancamento_path, alert: 'Visita não identificada.') if id.blank? + return responder_erro('Visita não identificada.') if id.blank? anterior = client.visita(id) attrs = mudancas(anterior) + # Confirmar um valor idêntico não é erro no inline edit — só não há o que fazer. if attrs.empty? - return redirect_to(admin_edicao_lancamento_path, alert: 'Nenhuma alteração informada.') + return respond_to do |format| + format.json { render json: { ok: true, campos: [], visita: {} } } + format.html { redirect_to admin_edicao_lancamento_path, alert: 'Nenhuma alteração informada.' } + end end if attrs['status'].present? && STATUS_VALIDOS.exclude?(attrs['status']) - return redirect_to(admin_edicao_lancamento_path, alert: 'Status inválido.') + return responder_erro('Status inválido.') end atualizada = client.atualizar_visita(id, attrs) @@ -87,17 +103,41 @@ class Admin::EdicaoLancamentosController < ApplicationController request: request ) - redirect_to admin_edicao_lancamento_path, - notice: "Lançamento da NF #{atualizada['reference']} atualizado no SimpliRoute. " \ - 'O painel refletirá na próxima sincronização.' + respond_to do |format| + format.json do + render json: { ok: true, campos: attrs.keys, visita: atualizada.slice(*CAMPOS_EDITAVEIS) } + end + format.html do + redirect_to admin_edicao_lancamento_path, + notice: "Lançamento da NF #{atualizada['reference']} atualizado no SimpliRoute. " \ + 'O painel refletirá na próxima sincronização.' + end + end rescue SimpliRoute::Error => e - redirect_to admin_edicao_lancamento_path, alert: "Não foi possível atualizar: #{e.message}" + responder_erro("Não foi possível atualizar: #{e.message}", status: :bad_gateway) end - # GET /admin/edicao_lancamento/historico?visit_id=123 → JSON (best-effort). + # GET /admin/edicao_lancamento/historico?visit_id=123 → JSON com as DUAS fontes: + # o histórico da API do SimpliRoute (best-effort, costuma vir vazio) e a nossa + # trilha do AuditoriaLog (quem editou, quando, de→para). def historico authorize :edicao_lancamento - render json: { ok: true, historico: client.historico(params[:visit_id]) } + + id = params[:visit_id].to_s.strip + interno = AuditoriaLog.por_entidade('SimpliRoute::Visita') + .where(entidade_id: id) + .includes(:user).recentes.limit(50) + .map do |log| + { + usuario: log.user&.nome_display || '—', + acao: log.acao, + quando: log.created_at.in_time_zone.strftime('%d/%m/%Y %H:%M'), + de: log.dados_anteriores, + para: log.dados_novos + } + end + + render json: { ok: true, api: client.historico(id), interno: interno } end private @@ -163,6 +203,21 @@ class Admin::EdicaoLancamentosController < ApplicationController render json: { ok: false, erro: msg }, status: status end + # Erro do atualizar nos dois formatos (JSON p/ inline edit, HTML p/ fallback). + def responder_erro(msg, status: :unprocessable_entity) + respond_to do |format| + format.json { render json: { ok: false, erro: msg }, status: status } + format.html { redirect_to admin_edicao_lancamento_path, alert: msg } + end + end + + # Só aceita URL http(s) — evita injetar lixo no do card (mesmo + # filtro do foto_url do OperacaoMetricas). + def url_imagem(valor) + url = valor.to_s.strip + url.match?(%r{\Ahttps?://}i) ? url : nil + end + # Sem token não há o que fazer — avisa e volta. def garantir_configurado return if SimpliRoute.configurado? diff --git a/app/views/admin/edicao_lancamentos/show.html.erb b/app/views/admin/edicao_lancamentos/show.html.erb index f9d45fc..20a8158 100644 --- a/app/views/admin/edicao_lancamentos/show.html.erb +++ b/app/views/admin/edicao_lancamentos/show.html.erb @@ -1,10 +1,18 @@ <%# app/views/admin/edicao_lancamentos/show.html.erb %> -
+<%# Card estilizado com foto da fachada, edição inline (click-to-edit, salva por + campo direto na API do SimpliRoute) e histórico duplo (nosso AuditoriaLog + + /history/ da API). Os dados do card chegam via fetch (endpoint buscar). %> +
<%# Header %>

Editar Lançamento

-

Corrige status, motivo e comentários de uma entrega direto no SimpliRoute

+

Corrige status, motivo e dados de checkout de uma entrega direto no SimpliRoute

<%# Flash %> @@ -13,9 +21,9 @@ <%# Aviso de sincronização %>
⚠️ -

As alterações são gravadas no SimpliRoute na hora, mas o painel e as - consolidações só refletem após a próxima sincronização da base de rastreio. - Toda alteração fica registrada na auditoria.

+

As alterações são gravadas no SimpliRoute na hora (cada campo salva ao confirmar ✓), + mas o painel e as consolidações só refletem após a próxima sincronização da base de + rastreio. Toda alteração fica registrada na auditoria.

<%# Busca por NF %> @@ -35,156 +43,380 @@
- <%# Formulário de edição (oculto até achar a visita) %> - <%= form_with url: atualizar_admin_edicao_lancamento_path, method: :patch, - html: { id: 'form-edicao', class: 'hidden bg-[#111] border border-white/5 rounded-2xl p-5 space-y-5' } do %> - + <%# Card do lançamento (preenchido via JS após a busca) %> + + + <%# Modal de histórico %> +