Files
Reem-Notas/app/views/admin/romaneios/show.html.erb

131 lines
7.0 KiB
Plaintext

<%# app/views/admin/romaneios/show.html.erb
Editor do romaneio: veículos à esquerda, tabela editável à direita, e o PDF
REAL numa sobreposição aberta sob demanda.
A prévia é um <iframe> de mesma origem, e isso não é preferência: o CSP deste
app está em modo enforcing com `object_src :none` (config/initializers/
content_security_policy.rb), o que bloqueia <embed> e <object>; não há
`frame_src`, então ele cai em `default_src :self` e o iframe passa. Uma data:
URL seria barrada pelo mesmo fallback. %>
<div class="space-y-5"
data-controller="romaneio"
data-romaneio-url-value="<%= admin_romaneio_path(@romaneio) %>"
data-romaneio-pdf-url-value="<%= pdf_admin_romaneio_path(@romaneio) %>"
data-romaneio-veiculo-value="<%= @veiculo %>">
<div class="flex flex-col sm:flex-row sm:flex-wrap sm:items-start sm:justify-between gap-3">
<div>
<h1 class="text-2xl font-bold text-white">
Romaneio de <%= l @romaneio.planned_date, format: '%d/%m/%Y' %>
</h1>
<p class="text-gray-400 text-sm mt-0.5">
<%= @romaneio.operacao_label || 'Sem operação vinculada' %> ·
<%= @romaneio.contagem_por_veiculo.values.sum %> paradas em
<%= @veiculos.size %> veículo(s) ·
importado de <%= @romaneio.origem %>
<%= "em #{l @romaneio.importado_em, format: '%d/%m %H:%M'}" if @romaneio.importado_em %>
</p>
</div>
<%# NOVOS x RECORRENTES: o mesmo aviso que o programa antigo dava numa caixa
de diálogo depois de aplicar o status. Fica ao lado do título porque é a
primeira coisa a conferir — "0 novos" quase sempre significa importação
sem operação vinculada, e o aviso azul abaixo explica o que fazer. %>
<% contagem = @romaneio.contagem_aparelho %>
<div class="flex items-center gap-2 text-sm">
<span class="px-3 py-1.5 rounded-xl border
<%= contagem[:novos].positive? ? 'bg-green-600/15 border-green-500/40 text-green-300'
: 'bg-amber-900/20 border-amber-500/40 text-amber-200' %>">
<strong><%= contagem[:novos] %></strong> com aparelho (NOVOS)
</span>
<span class="px-3 py-1.5 rounded-xl bg-white/5 border border-white/10 text-gray-300">
<strong><%= contagem[:recorrentes] %></strong> recorrentes
</span>
</div>
<div class="flex flex-wrap gap-2">
<%= link_to rotulo(:baixar, 'Baixar todos'),
pdf_admin_romaneio_path(@romaneio),
class: 'px-4 py-2.5 bg-[#f97316] hover:bg-orange-500 text-white font-semibold rounded-xl min-h-[44px] inline-flex items-center' %>
<%# Em BLOCO: button_to sem bloco gera <input type="submit">, que não aceita
o SVG do ícone (ver ApplicationHelper#rotulo). %>
<%= button_to reimportar_admin_romaneio_path(@romaneio, veiculo: @veiculo), method: :post,
form: { data: { turbo_confirm: 'Rebuscar o plano? Os campos que você editou são preservados.' } },
class: 'px-4 py-2.5 bg-[#1a1a1a] hover:bg-[#242424] text-white border border-white/10 rounded-xl min-h-[44px]' do %>
<%= icone :restaurar, cor: nil %> Reimportar plano
<% end %>
<%= link_to 'Voltar', admin_romaneios_path,
class: 'px-4 py-2.5 text-gray-400 hover:text-white min-h-[44px] inline-flex items-center' %>
</div>
</div>
<%= render 'shared/flash' %>
<% if @veiculos.empty? %>
<div class="bg-[#111] border border-white/5 rounded-2xl p-8 text-center">
<p class="text-gray-400">Este romaneio está sem paradas.</p>
<p class="text-gray-500 text-sm mt-1">Clique em “Reimportar plano” ou envie a planilha do plano na tela anterior.</p>
</div>
<% else %>
<%= render 'avisos' %>
<%# Duas zonas na tela: VEÍCULOS e a edição. A prévia saiu do fluxo e virou
sobreposição sob demanda (botão "Ver prévia") — antes o <iframe> era montado
em TODO carregamento, e cada montagem gerava um PDF no servidor só para
ficar num canto que o operador nem sempre olha. Agora o PDF só é gerado
quando alguém pede para ver. %>
<div class="grid grid-cols-1 lg:grid-cols-[minmax(0,260px)_minmax(0,1fr)] gap-5 items-start">
<div class="lg:sticky lg:top-4">
<%= render 'veiculos' %>
</div>
<div class="space-y-4">
<%= render 'controles' %>
<%= render 'tabela', linhas: @linhas %>
<%= render 'removidas', linhas: @removidas %>
</div>
</div>
<%# ── Prévia em sobreposição ──────────────────────────────────────────────
Fica FORA da grade, ocupando a tela inteira quando aberta. `hidden` no
markup e o src só preenchido na abertura: um iframe com src carrega
mesmo escondido, o que anularia a economia.
<iframe> e não <embed>/<object>: o CSP está em enforcing com
`object_src :none`, e sem `frame_src` ele cai em `default_src :self` —
o iframe de mesma origem passa, os outros dois não.
O fechado/aberto é pela CLASSE `hidden`/`flex`, não só pelo atributo
`hidden`: as utilities do Tailwind são aplicadas depois do preflight,
então `.flex` vence o `[hidden] { display:none }` — com `flex` fixo no
markup o modal nascia aberto, em branco e sem fechar, cobrindo a tela
inteira (caso real: /admin/romaneios/13). O controller troca as classes
e mantém o atributo sincronizado. %>
<div data-romaneio-target="overlay" hidden
data-action="click->romaneio#fecharPorFora keydown.esc@window->romaneio#fecharPreview"
class="fixed inset-0 z-50 hidden items-center justify-center p-3 sm:p-6
bg-black/70 backdrop-blur-sm">
<div class="w-full max-w-6xl h-[92vh] flex flex-col gap-3"
data-romaneio-target="caixaPreview">
<div class="flex items-center justify-between gap-3">
<h2 class="text-white font-semibold">
Prévia — <span data-romaneio-target="tituloPreview"><%= @veiculo %></span>
</h2>
<div class="flex items-center gap-2">
<%= link_to 'Abrir em outra aba',
pdf_admin_romaneio_path(@romaneio, veiculo: @veiculo),
target: '_blank', rel: 'noopener',
class: 'text-sm text-gray-300 hover:text-white px-3 py-2 rounded-lg hover:bg-white/10' %>
<button type="button" data-action="romaneio#fecharPreview"
aria-label="Fechar prévia"
class="min-w-[48px] min-h-[48px] rounded-xl bg-[#1a1a1a] border border-white/10
text-white hover:bg-[#242424]">✕</button>
</div>
</div>
<iframe data-romaneio-target="preview"
class="flex-1 w-full rounded-xl border border-white/10 bg-white"
title="Pré-visualização do romaneio"></iframe>
</div>
</div>
<% end %>
</div>