, correção para gerara prévia do romaneio
This commit is contained in:
44
app/views/admin/romaneios/_previa.html.erb
Normal file
44
app/views/admin/romaneios/_previa.html.erb
Normal file
@@ -0,0 +1,44 @@
|
||||
<%# ── Prévia do PDF ───────────────────────────────────────────────────────
|
||||
<details> NATIVO, e não um modal controlado por Stimulus. Isto é o
|
||||
conserto de um problema que voltou três vezes: a prévia era o ÚNICO jeito
|
||||
de conferir a folha antes de imprimir e morria junto com o JavaScript.
|
||||
Quando os assets do servidor ficaram 404 (precompile que não rodou no
|
||||
deploy), o botão "Ver prévia do PDF" não fazia nada e o operador ficava
|
||||
sem saída — a tela abria normal, sem erro visível. Com <details> o
|
||||
abrir/fechar é do navegador: funciona com o JS quebrado, sem JS, e no
|
||||
celular.
|
||||
|
||||
`loading="lazy"`: o iframe só busca o PDF quando o bloco é aberto. É o que
|
||||
preserva a economia que o modal trazia (gerar PDF a cada carregamento da
|
||||
tela era o problema original) sem precisar de JS para preencher o `src`.
|
||||
|
||||
<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 `data-romaneio-target="preview"` continua aqui de propósito: com o JS
|
||||
no ar, `atualizarPreview()` recarrega a folha depois de cada edição. Sem
|
||||
ele, basta fechar e abrir. %>
|
||||
<details class="bg-[#111] border border-white/5 rounded-2xl overflow-hidden"
|
||||
data-romaneio-target="overlay"
|
||||
data-action="toggle->romaneio#aoAbrirPreview">
|
||||
<summary class="cursor-pointer list-none px-4 py-3 min-h-[48px] flex items-center gap-2
|
||||
text-white hover:bg-white/5">
|
||||
<%= icone :visualizar, cor: nil, espaco: false %>
|
||||
Prévia do PDF — <span data-romaneio-target="tituloPreview"><%= @veiculo %></span>
|
||||
<span class="ml-auto text-xs text-gray-500">clique para abrir / fechar</span>
|
||||
</summary>
|
||||
|
||||
<div class="px-4 pb-4 space-y-2">
|
||||
<div class="flex justify-end">
|
||||
<%= 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' %>
|
||||
</div>
|
||||
<iframe data-romaneio-target="preview" loading="lazy"
|
||||
src="<%= pdf_admin_romaneio_path(@romaneio, veiculo: @veiculo) %>"
|
||||
class="w-full h-[80vh] rounded-xl border border-white/10 bg-white"
|
||||
title="Pré-visualização do romaneio"></iframe>
|
||||
</div>
|
||||
</details>
|
||||
@@ -63,12 +63,10 @@
|
||||
</div>
|
||||
|
||||
<div class="space-y-2 pt-1">
|
||||
<%# Prévia sob demanda: o PDF só é gerado quando alguém pede para ver. %>
|
||||
<button type="button" data-action="romaneio#abrirPreview"
|
||||
class="w-full min-h-[48px] flex items-center justify-center gap-2 bg-[#1a1a1a]
|
||||
hover:bg-[#242424] text-white border border-white/10 rounded-xl">
|
||||
<%= icone :visualizar, cor: nil %> Ver prévia do PDF
|
||||
</button>
|
||||
<%# A prévia NÃO tem botão aqui: ela é o bloco recolhível no topo da coluna
|
||||
ao lado (`_previa`), aberto pelo próprio navegador. Um segundo botão que
|
||||
dependesse de JavaScript para abrir aquele bloco voltaria a ficar morto
|
||||
quando os assets falhassem — que é justamente o que se está evitando. %>
|
||||
<%= link_to pdf_admin_romaneio_path(@romaneio, veiculo: @veiculo),
|
||||
target: '_blank', rel: 'noopener',
|
||||
class: 'w-full min-h-[48px] flex items-center justify-center gap-2 bg-[#1a1a1a]
|
||||
|
||||
@@ -55,7 +55,19 @@
|
||||
<div class="bg-[#111] border border-white/5 rounded-2xl p-5 space-y-4">
|
||||
<h2 class="text-white font-semibold">Importar plano</h2>
|
||||
|
||||
<%# UM formulário com DUAS actions: "Buscar no SimpliRoute" usa a action do
|
||||
<form> e "Enviar planilha" a troca por `formaction`, para não repetir data,
|
||||
operação e rótulo em dois formulários que sairiam de sincronia.
|
||||
|
||||
`authenticity_token: form_authenticity_token` é o que faz o segundo botão
|
||||
funcionar. Com `load_defaults 7.1` o Rails liga `per_form_csrf_tokens`: o
|
||||
token embutido vale só para o par (action, method) do <form>, então o POST
|
||||
que o `formaction` manda para /importar_planilha era recusado SEMPRE, com a
|
||||
tela "A ação foi recusada por uma verificação de segurança" e volta ao
|
||||
início — enquanto o botão de buscar, na action original, passava. O helper
|
||||
sem argumentos devolve o token GLOBAL, válido para as duas actions. %>
|
||||
<%= form_with url: admin_romaneios_path, method: :post, multipart: true,
|
||||
authenticity_token: form_authenticity_token,
|
||||
data: { turbo: false }, class: 'space-y-4' do %>
|
||||
<div class="grid grid-cols-1 sm:grid-cols-3 gap-3">
|
||||
<div>
|
||||
|
||||
@@ -1,12 +1,7 @@
|
||||
<%# 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. %>
|
||||
REAL numa prévia recolhível no topo da coluna de edição (partial `_previa`,
|
||||
que explica por que ela é <details> nativo e não um modal com JavaScript). %>
|
||||
<div class="space-y-5"
|
||||
data-controller="romaneio"
|
||||
data-romaneio-url-value="<%= admin_romaneio_path(@romaneio) %>"
|
||||
@@ -69,63 +64,22 @@
|
||||
<%= render 'operacao' %>
|
||||
<%= 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. %>
|
||||
<%# Duas zonas na tela: VEÍCULOS e a edição. A prévia é o bloco fechado no topo
|
||||
da coluna de edição — o PDF só é gerado quando alguém o abre (antes o
|
||||
<iframe> era montado em TODO carregamento, gerando um PDF no servidor para
|
||||
ficar num canto que o operador nem sempre olha). %>
|
||||
<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 'previa' %>
|
||||
<%= 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>
|
||||
|
||||
Reference in New Issue
Block a user