, correção de UX para gerara prévia do romaneio
This commit is contained in:
32
app/assets/tailwind/application.css
Normal file
32
app/assets/tailwind/application.css
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
/* app/assets/tailwind/application.css
|
||||||
|
*
|
||||||
|
* ESTE ARQUIVO EXISTE PARA O DEPLOY NÃO QUEBRAR. Leia antes de apagar.
|
||||||
|
*
|
||||||
|
* A gem `tailwindcss-rails` está no Gemfile e pendura a tarefa `tailwindcss:build`
|
||||||
|
* dentro de `assets:precompile`. Ela procura ESTE caminho fixo e, se não achar,
|
||||||
|
* aborta com:
|
||||||
|
*
|
||||||
|
* Specified input file `./app/assets/tailwind/application.css` does not exist.
|
||||||
|
* Tasks: TOP => assets:precompile => tailwindcss:build
|
||||||
|
*
|
||||||
|
* E o `assets:precompile` morre INTEIRO nessa linha — nenhum arquivo chega em
|
||||||
|
* public/assets. O efeito na tela é traiçoeiro: o Propshaft, sem manifesto, cai
|
||||||
|
* no resolvedor dinâmico e continua montando URLs com o digest certo, então a
|
||||||
|
* página abre normal e CADA /assets/*.js devolve 404 em silêncio. O sistema fica
|
||||||
|
* sem JavaScript nenhum: prévia do PDF, edição inline da tabela, busca e
|
||||||
|
* carrossel todos mortos, sem erro visível. Foi o que aconteceu de 27 a
|
||||||
|
* 28/08/2026 e custou três rodadas de diagnóstico.
|
||||||
|
*
|
||||||
|
* POR QUE ESTÁ VAZIO: o CSS do sistema hoje vem do CDN do Tailwind, carregado em
|
||||||
|
* app/views/layouts/application.html.erb. Sem `@import "tailwindcss"` aqui, o
|
||||||
|
* build termina na hora e gera um arquivo vazio — nada é servido a mais e o
|
||||||
|
* deploy não perde tempo montando um CSS que ninguém carrega.
|
||||||
|
*
|
||||||
|
* DÍVIDA CONHECIDA (uma das duas, quando houver espaço):
|
||||||
|
* a) migrar do CDN para o Tailwind local — o próprio Tailwind desaconselha o
|
||||||
|
* CDN em produção, e ele deixa o sistema dependendo de rede externa. Neste
|
||||||
|
* caso, troque este comentário por `@import "tailwindcss";` e o
|
||||||
|
* <script src="https://cdn.tailwindcss.com"> por stylesheet_link_tag; ou
|
||||||
|
* b) remover `tailwindcss-rails` do Gemfile, que hoje é dependência morta.
|
||||||
|
* Aí este arquivo some junto.
|
||||||
|
*/
|
||||||
@@ -131,10 +131,15 @@ export default class extends Controller {
|
|||||||
this.buscaTarget.focus()
|
this.buscaTarget.focus()
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Prévia (bloco <details> nativo) ─────────────────────────────────────
|
// ── Prévia (pop-up por :target, CSS puro) ───────────────────────────────
|
||||||
// Abrir e fechar é do NAVEGADOR — o <details> não depende deste controller,
|
// Abrir, fechar e trocar de folha são do NAVEGADOR: link para `#previa`,
|
||||||
// e é isso que mantém a prévia viva quando o JavaScript não carrega. O que
|
// link para `#sem-previa` e links `?veiculo=X#previa`. Nada disso depende
|
||||||
// sobra aqui é só recarregar a folha depois de uma edição.
|
// deste controller — é o que mantém a prévia viva quando o JS não carrega.
|
||||||
|
// O que sobra aqui é recarregar a folha depois de uma edição.
|
||||||
|
previewAberta() {
|
||||||
|
return this.hasOverlayTarget && this.overlayTarget.matches(":target")
|
||||||
|
}
|
||||||
|
|
||||||
carregarPreview() {
|
carregarPreview() {
|
||||||
if (!this.hasPreviewTarget) return
|
if (!this.hasPreviewTarget) return
|
||||||
|
|
||||||
@@ -145,23 +150,21 @@ export default class extends Controller {
|
|||||||
this.previewDesatualizado = false
|
this.previewDesatualizado = false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Chamada depois de cada gravação. Com o bloco FECHADO não gera PDF nenhum —
|
// Chamada depois de cada gravação. Com o pop-up FECHADO não gera PDF nenhum —
|
||||||
// o `loading="lazy"` do iframe já garante isso, e recarregar o src de um
|
// o `loading="lazy"` do iframe já garante isso, e recarregar o src de um
|
||||||
// iframe escondido forçaria o servidor a montar um PDF que ninguém vai ver.
|
// iframe escondido forçaria o servidor a montar um PDF que ninguém vai ver.
|
||||||
atualizarPreview() {
|
atualizarPreview() {
|
||||||
if (!this.hasOverlayTarget || !this.overlayTarget.open) {
|
if (!this.previewAberta()) {
|
||||||
this.previewDesatualizado = true
|
this.previewDesatualizado = true
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
this.carregarPreview()
|
this.carregarPreview()
|
||||||
}
|
}
|
||||||
|
|
||||||
// O operador pode abrir a prévia depois de ter editado: neste caso o iframe
|
// O operador pode abrir a prévia DEPOIS de editar: aí o iframe ainda mostra a
|
||||||
// ainda mostra a folha antiga, então recarregamos no momento da abertura.
|
// folha antiga. Como quem abre é o `:target`, o gancho é o hashchange.
|
||||||
aoAbrirPreview() {
|
aoTrocarHash() {
|
||||||
if (this.hasOverlayTarget && this.overlayTarget.open && this.previewDesatualizado) {
|
if (this.previewAberta() && this.previewDesatualizado) this.carregarPreview()
|
||||||
this.carregarPreview()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Infra ───────────────────────────────────────────────────────────────
|
// ── Infra ───────────────────────────────────────────────────────────────
|
||||||
|
|||||||
@@ -1,44 +1,101 @@
|
|||||||
<%# ── Prévia do PDF ───────────────────────────────────────────────────────
|
<%# Prévia do PDF em POP-UP, com navegação de veículo igual à do carrossel do
|
||||||
<details> NATIVO, e não um modal controlado por Stimulus. Isto é o
|
motorista (setas grandes + contador "3 de 72").
|
||||||
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
|
── POR QUE É CSS PURO E NÃO JAVASCRIPT ────────────────────────────────────
|
||||||
preserva a economia que o modal trazia (gerar PDF a cada carregamento da
|
Abrir/fechar sai do `:target`: o botão é um link para `#previa` e o pop-up só
|
||||||
tela era o problema original) sem precisar de JS para preencher o `src`.
|
aparece quando ele é o alvo da URL. As setas são links de verdade para
|
||||||
|
`?veiculo=X#previa`, então trocar de folha mantém o pop-up aberto.
|
||||||
|
|
||||||
<iframe> e não <embed>/<object>: o CSP está em enforcing com
|
Isso não é preciosismo: a prévia é a ÚNICA conferência antes de imprimir e
|
||||||
`object_src :none`, e sem `frame_src` ele cai em `default_src :self` — o
|
já morreu três vezes junto com o JavaScript (assets 404 no servidor, com a
|
||||||
iframe de mesma origem passa, os outros dois não.
|
tela abrindo normal e o botão sem reação). Do jeito atual ela funciona com o
|
||||||
|
JS quebrado, sem JS, no celular, e continua endereçável — dá para mandar o
|
||||||
|
link já com a prévia aberta.
|
||||||
|
|
||||||
O `data-romaneio-target="preview"` continua aqui de propósito: com o JS
|
`loading="lazy"` no iframe: fechado, o pop-up é `display:none` e o navegador
|
||||||
no ar, `atualizarPreview()` recarrega a folha depois de cada edição. Sem
|
NÃO busca o PDF. É o que preserva a economia de não gerar um documento a cada
|
||||||
ele, basta fechar e abrir. %>
|
carregamento da tela.
|
||||||
<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">
|
<iframe> e não <embed>/<object>: o CSP está enforcing com `object_src :none`
|
||||||
<div class="flex justify-end">
|
e sem `frame_src`, então cai em `default_src :self` — o iframe de mesma
|
||||||
<%= link_to 'Abrir em outra aba',
|
origem passa, os outros dois não.
|
||||||
pdf_admin_romaneio_path(@romaneio, veiculo: @veiculo),
|
|
||||||
target: '_blank', rel: 'noopener',
|
Fechar leva para `#sem-previa`, um id que não existe em lugar nenhum: o
|
||||||
class: 'text-sm text-gray-300 hover:text-white px-3 py-2 rounded-lg hover:bg-white/10' %>
|
`:target` deixa de casar e o pop-up some sem o salto ao topo que um `href="#"`
|
||||||
|
causaria. %>
|
||||||
|
<% nomes = @veiculos.map(&:veiculo) %>
|
||||||
|
<% posicao = nomes.index(@veiculo) %>
|
||||||
|
<% anterior = posicao&.positive? ? nomes[posicao - 1] : nil %>
|
||||||
|
<% proximo = posicao && posicao < nomes.size - 1 ? nomes[posicao + 1] : nil %>
|
||||||
|
|
||||||
|
<div id="previa" data-romaneio-target="overlay"
|
||||||
|
class="hidden target:flex fixed inset-0 z-50 items-center justify-center p-3 sm:p-6
|
||||||
|
bg-black/80 backdrop-blur-sm">
|
||||||
|
|
||||||
|
<%# O fundo escuro fecha ao ser clicado — um link cobrindo a área, atrás da
|
||||||
|
caixa (que é `relative`). Sem isso, só o ✕ fecharia. %>
|
||||||
|
<a href="#sem-previa" tabindex="-1" aria-label="Fechar prévia" class="absolute inset-0"></a>
|
||||||
|
|
||||||
|
<div class="relative w-full max-w-6xl h-[92vh] flex flex-col gap-3">
|
||||||
|
<div class="flex items-center justify-between gap-3">
|
||||||
|
<h2 class="text-white font-semibold truncate">
|
||||||
|
Prévia — <span data-romaneio-target="tituloPreview"><%= @veiculo %></span>
|
||||||
|
</h2>
|
||||||
|
<div class="flex items-center gap-2 shrink-0">
|
||||||
|
<%= 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' %>
|
||||||
|
<a href="#sem-previa" aria-label="Fechar prévia"
|
||||||
|
class="min-w-[48px] min-h-[48px] flex items-center justify-center rounded-xl
|
||||||
|
bg-[#1a1a1a] border border-white/10 text-white hover:bg-[#242424]">✕</a>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<iframe data-romaneio-target="preview" loading="lazy"
|
<iframe data-romaneio-target="preview" loading="lazy"
|
||||||
src="<%= pdf_admin_romaneio_path(@romaneio, veiculo: @veiculo) %>"
|
src="<%= pdf_admin_romaneio_path(@romaneio, veiculo: @veiculo) %>"
|
||||||
class="w-full h-[80vh] rounded-xl border border-white/10 bg-white"
|
class="flex-1 w-full rounded-xl border border-white/10 bg-white"
|
||||||
title="Pré-visualização do romaneio"></iframe>
|
title="Pré-visualização do romaneio"></iframe>
|
||||||
|
|
||||||
|
<%# Controles no mesmo desenho do carrossel do motorista (redondos, 56px,
|
||||||
|
próximo em laranja), mas como LINKS: cada folha é uma URL. Na ponta vira
|
||||||
|
<span> apagado em vez de sumir — botão que some muda o layout no meio da
|
||||||
|
conferência e o operador perde o alvo do clique. %>
|
||||||
|
<div class="flex items-center justify-center gap-3">
|
||||||
|
<% if anterior %>
|
||||||
|
<%= link_to admin_romaneio_path(@romaneio, veiculo: anterior, anchor: 'previa'),
|
||||||
|
'aria-label': "Folha anterior (#{anterior})",
|
||||||
|
class: 'w-14 h-14 rounded-full flex items-center justify-center
|
||||||
|
bg-[#1a1a1a]/90 backdrop-blur border border-white/15 text-white
|
||||||
|
hover:border-orange-500 transition-colors' do %>
|
||||||
|
<%= icone :voltar, cor: nil, tamanho: 'w-6 h-6', espaco: false %>
|
||||||
|
<% end %>
|
||||||
|
<% else %>
|
||||||
|
<span class="w-14 h-14 rounded-full flex items-center justify-center
|
||||||
|
bg-[#1a1a1a]/50 border border-white/10 text-gray-600 opacity-40">
|
||||||
|
<%= icone :voltar, cor: nil, tamanho: 'w-6 h-6', espaco: false %>
|
||||||
|
</span>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
<span class="bg-[#1a1a1a] border border-[#2a2a2a] text-gray-200 text-sm font-semibold
|
||||||
|
rounded-full px-4 py-2 whitespace-nowrap">
|
||||||
|
<%= posicao ? posicao + 1 : 1 %> de <%= nomes.size %> · <%= @veiculo %>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<% if proximo %>
|
||||||
|
<%= link_to admin_romaneio_path(@romaneio, veiculo: proximo, anchor: 'previa'),
|
||||||
|
'aria-label': "Próxima folha (#{proximo})",
|
||||||
|
class: 'w-14 h-14 rounded-full flex items-center justify-center
|
||||||
|
bg-orange-500/90 backdrop-blur border border-orange-400 text-black
|
||||||
|
hover:bg-orange-500 transition-colors' do %>
|
||||||
|
<%= icone :avancar, cor: nil, tamanho: 'w-6 h-6', espaco: false %>
|
||||||
|
<% end %>
|
||||||
|
<% else %>
|
||||||
|
<span class="w-14 h-14 rounded-full flex items-center justify-center
|
||||||
|
bg-orange-500/20 border border-orange-400/30 text-black/40 opacity-40">
|
||||||
|
<%= icone :avancar, cor: nil, tamanho: 'w-6 h-6', espaco: false %>
|
||||||
|
</span>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</details>
|
</div>
|
||||||
|
|||||||
@@ -38,17 +38,30 @@
|
|||||||
<% end %>
|
<% end %>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex gap-2">
|
<%# Setas para andar na LISTA (a prévia tem as suas). Links de verdade pelo
|
||||||
<button type="button" data-action="romaneio#anterior" aria-label="Veículo anterior"
|
mesmo motivo dos itens acima: o veículo mora na query string. %>
|
||||||
class="flex-1 min-h-[48px] bg-[#1a1a1a] border border-white/10 rounded-xl
|
<div class="flex gap-2 pt-1">
|
||||||
text-white hover:bg-[#242424]">‹</button>
|
<% nomes_v = @veiculos.map(&:veiculo) %>
|
||||||
<button type="button" data-action="romaneio#proximo" aria-label="Próximo veículo"
|
<% pos_v = nomes_v.index(@veiculo) %>
|
||||||
class="flex-1 min-h-[48px] bg-[#1a1a1a] border border-white/10 rounded-xl
|
<% ant_v = pos_v&.positive? ? nomes_v[pos_v - 1] : nil %>
|
||||||
text-white hover:bg-[#242424]">›</button>
|
<% prox_v = pos_v && pos_v < nomes_v.size - 1 ? nomes_v[pos_v + 1] : nil %>
|
||||||
|
<% base_v = 'flex-1 min-h-[48px] flex items-center justify-center rounded-xl border' %>
|
||||||
|
<% if ant_v %>
|
||||||
|
<a href="<%= admin_romaneio_path(@romaneio, veiculo: ant_v) %>" aria-label="Veículo anterior"
|
||||||
|
class="<%= base_v %> bg-[#1a1a1a] border-white/10 text-white hover:bg-[#242424]">‹</a>
|
||||||
|
<% else %>
|
||||||
|
<span class="<%= base_v %> bg-[#1a1a1a]/50 border-white/5 text-gray-600">‹</span>
|
||||||
|
<% end %>
|
||||||
|
<% if prox_v %>
|
||||||
|
<a href="<%= admin_romaneio_path(@romaneio, veiculo: prox_v) %>" aria-label="Próximo veículo"
|
||||||
|
class="<%= base_v %> bg-[#1a1a1a] border-white/10 text-white hover:bg-[#242424]">›</a>
|
||||||
|
<% else %>
|
||||||
|
<span class="<%= base_v %> bg-[#1a1a1a]/50 border-white/5 text-gray-600">›</span>
|
||||||
|
<% end %>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="pt-1 border-t border-white/5">
|
<div class="pt-4 mt-1 border-t border-white/5">
|
||||||
<label class="block text-sm font-medium text-gray-300 mb-1.5 mt-3">
|
<label class="block text-sm font-medium text-gray-300 mb-1.5">
|
||||||
Motorista deste veículo
|
Motorista deste veículo
|
||||||
<span class="text-gray-500 font-normal">— só na tela</span>
|
<span class="text-gray-500 font-normal">— só na tela</span>
|
||||||
</label>
|
</label>
|
||||||
@@ -62,21 +75,25 @@
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="space-y-2 pt-1">
|
<div class="space-y-3 pt-4 mt-4 border-t border-white/5">
|
||||||
<%# A prévia NÃO tem botão aqui: ela é o bloco recolhível no topo da coluna
|
<%# Link, não botão: abre o pop-up da prévia pelo `:target` do CSS (ver
|
||||||
ao lado (`_previa`), aberto pelo próprio navegador. Um segundo botão que
|
`_previa`), então continua funcionando com o JavaScript fora do ar — foi
|
||||||
dependesse de JavaScript para abrir aquele bloco voltaria a ficar morto
|
o botão daqui que ficou morto três vezes seguidas. %>
|
||||||
quando os assets falhassem — que é justamente o que se está evitando. %>
|
<a href="#previa"
|
||||||
|
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, espaco: false %> Ver prévia do PDF
|
||||||
|
</a>
|
||||||
<%= link_to pdf_admin_romaneio_path(@romaneio, veiculo: @veiculo),
|
<%= link_to pdf_admin_romaneio_path(@romaneio, veiculo: @veiculo),
|
||||||
target: '_blank', rel: 'noopener',
|
target: '_blank', rel: 'noopener',
|
||||||
class: 'w-full min-h-[48px] flex items-center justify-center gap-2 bg-[#1a1a1a]
|
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' do %>
|
hover:bg-[#242424] text-white border border-white/10 rounded-xl' do %>
|
||||||
<%= icone :baixar, cor: nil %> PDF deste veículo
|
<%= icone :baixar, cor: nil, espaco: false %> PDF deste veículo
|
||||||
<% end %>
|
<% end %>
|
||||||
<%= link_to pdf_admin_romaneio_path(@romaneio),
|
<%= link_to pdf_admin_romaneio_path(@romaneio),
|
||||||
class: 'w-full min-h-[48px] flex items-center justify-center gap-2 bg-[#f97316]
|
class: 'w-full min-h-[48px] flex items-center justify-center gap-2 bg-[#f97316]
|
||||||
hover:bg-orange-500 text-white font-semibold rounded-xl' do %>
|
hover:bg-orange-500 text-white font-semibold rounded-xl' do %>
|
||||||
<%= icone :baixar, cor: nil %> PDF de TODOS os veículos
|
<%= icone :baixar, cor: nil, espaco: false %> PDF de TODOS os veículos
|
||||||
<% end %>
|
<% end %>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
<%# app/views/admin/romaneios/show.html.erb
|
<%# app/views/admin/romaneios/show.html.erb
|
||||||
Editor do romaneio: veículos à esquerda, tabela editável à direita, e o PDF
|
Editor do romaneio: veículos à esquerda, tabela editável à direita, e o PDF
|
||||||
REAL numa prévia recolhível no topo da coluna de edição (partial `_previa`,
|
REAL num pop-up com navegação entre as folhas (partial `_previa`, que explica
|
||||||
que explica por que ela é <details> nativo e não um modal com JavaScript). %>
|
por que abrir/fechar/trocar de folha é CSS puro e não JavaScript). %>
|
||||||
<div class="space-y-5"
|
<div class="space-y-5"
|
||||||
data-controller="romaneio"
|
data-controller="romaneio"
|
||||||
|
data-action="hashchange@window->romaneio#aoTrocarHash"
|
||||||
data-romaneio-url-value="<%= admin_romaneio_path(@romaneio) %>"
|
data-romaneio-url-value="<%= admin_romaneio_path(@romaneio) %>"
|
||||||
data-romaneio-pdf-url-value="<%= pdf_admin_romaneio_path(@romaneio) %>"
|
data-romaneio-pdf-url-value="<%= pdf_admin_romaneio_path(@romaneio) %>"
|
||||||
data-romaneio-veiculo-value="<%= @veiculo %>">
|
data-romaneio-veiculo-value="<%= @veiculo %>">
|
||||||
@@ -64,22 +65,24 @@
|
|||||||
<%= render 'operacao' %>
|
<%= render 'operacao' %>
|
||||||
<%= render 'avisos' %>
|
<%= render 'avisos' %>
|
||||||
|
|
||||||
<%# Duas zonas na tela: VEÍCULOS e a edição. A prévia é o bloco fechado no topo
|
<%# Duas zonas na tela: VEÍCULOS e a edição. A prévia é o pop-up mais abaixo —
|
||||||
da coluna de edição — o PDF só é gerado quando alguém o abre (antes o
|
o PDF só é gerado quando alguém o abre (antes o <iframe> era montado em
|
||||||
<iframe> era montado em TODO carregamento, gerando um PDF no servidor para
|
TODO carregamento, gerando um PDF no servidor para ficar num canto que o
|
||||||
ficar num canto que o operador nem sempre olha). %>
|
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="grid grid-cols-1 lg:grid-cols-[minmax(0,260px)_minmax(0,1fr)] gap-5 items-start">
|
||||||
<div class="lg:sticky lg:top-4">
|
<div class="lg:sticky lg:top-4">
|
||||||
<%= render 'veiculos' %>
|
<%= render 'veiculos' %>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="space-y-4">
|
<div class="space-y-4">
|
||||||
<%= render 'previa' %>
|
|
||||||
<%= render 'controles' %>
|
<%= render 'controles' %>
|
||||||
<%= render 'tabela', linhas: @linhas %>
|
<%= render 'tabela', linhas: @linhas %>
|
||||||
<%= render 'removidas', linhas: @removidas %>
|
<%= render 'removidas', linhas: @removidas %>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<%# Pop-up da prévia: FORA da grade porque cobre a tela inteira quando
|
||||||
|
aberto (`:target`). Fechado não ocupa espaço nem busca o PDF. %>
|
||||||
|
<%= render 'previa' %>
|
||||||
<% end %>
|
<% end %>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -141,6 +141,15 @@ if [ "${RAILS_ENV:-development}" = "production" ]; then
|
|||||||
log "ERRO: assets:precompile falhou — o sistema vai subir SEM JS/CSS novos."
|
log "ERRO: assets:precompile falhou — o sistema vai subir SEM JS/CSS novos."
|
||||||
log " Rode 'docker compose exec app bundle exec rails assets:precompile'"
|
log " Rode 'docker compose exec app bundle exec rails assets:precompile'"
|
||||||
log " e veja o erro completo."
|
log " e veja o erro completo."
|
||||||
|
# Causa já vista (28/08/2026), e que não se anuncia: a tarefa
|
||||||
|
# `tailwindcss:build` roda DENTRO do precompile e aborta tudo se
|
||||||
|
# app/assets/tailwind/application.css não existir. Se o arquivo sumir de
|
||||||
|
# novo, o log abaixo aponta direto para ele em vez de mandar caçar.
|
||||||
|
if [ ! -f app/assets/tailwind/application.css ]; then
|
||||||
|
log " CAUSA PROVÁVEL: falta app/assets/tailwind/application.css, exigido"
|
||||||
|
log " por tailwindcss:build (gem tailwindcss-rails). Leia o cabeçalho"
|
||||||
|
log " desse arquivo no repositório antes de recriá-lo."
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
# Em development o Propshaft calcula o digest do arquivo atual a cada
|
# Em development o Propshaft calcula o digest do arquivo atual a cada
|
||||||
|
|||||||
Reference in New Issue
Block a user