, correção para gerara prévia do romaneio
This commit is contained in:
@@ -10,8 +10,10 @@ import { Controller } from "@hotwired/stimulus"
|
|||||||
// plano inteiro. A regra de reconciliação (Romaneios::Importador) só funciona com
|
// plano inteiro. A regra de reconciliação (Romaneios::Importador) só funciona com
|
||||||
// escrita por campo.
|
// escrita por campo.
|
||||||
export default class extends Controller {
|
export default class extends Controller {
|
||||||
|
// `overlay` é o <details> da prévia (nome mantido para não renomear em vão);
|
||||||
|
// `caixaPreview` saiu junto com o modal.
|
||||||
static targets = ["celula", "preview", "itemVeiculo", "motorista", "rotulo", "status", "linha", "busca",
|
static targets = ["celula", "preview", "itemVeiculo", "motorista", "rotulo", "status", "linha", "busca",
|
||||||
"contador", "overlay", "caixaPreview", "tituloPreview"]
|
"contador", "overlay", "tituloPreview"]
|
||||||
static values = { url: String, pdfUrl: String, veiculo: String }
|
static values = { url: String, pdfUrl: String, veiculo: String }
|
||||||
|
|
||||||
// Recarregar o PDF a cada tecla travaria a tela; 600ms depois do último save é o
|
// Recarregar o PDF a cada tecla travaria a tela; 600ms depois do último save é o
|
||||||
@@ -129,38 +131,10 @@ export default class extends Controller {
|
|||||||
this.buscaTarget.focus()
|
this.buscaTarget.focus()
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Prévia (sobreposição) ───────────────────────────────────────────────
|
// ── Prévia (bloco <details> nativo) ─────────────────────────────────────
|
||||||
abrirPreview() {
|
// Abrir e fechar é do NAVEGADOR — o <details> não depende deste controller,
|
||||||
if (!this.hasOverlayTarget) return
|
// e é isso que mantém a prévia viva quando o JavaScript não carrega. O que
|
||||||
|
// sobra aqui é só recarregar a folha depois de uma edição.
|
||||||
// Classe + atributo: só o atributo `hidden` não basta, porque a utility
|
|
||||||
// `.flex` do Tailwind vence o `[hidden] { display:none }` do preflight —
|
|
||||||
// foi assim que o modal ficou preso na tela do /admin/romaneios/13.
|
|
||||||
this.overlayTarget.hidden = false
|
|
||||||
this.overlayTarget.classList.remove("hidden")
|
|
||||||
this.overlayTarget.classList.add("flex")
|
|
||||||
// Trava o scroll do fundo: rolar a lista atrás de um modal aberto faz o
|
|
||||||
// operador perder o lugar quando fecha.
|
|
||||||
document.body.style.overflow = "hidden"
|
|
||||||
if (this.previewDesatualizado) this.carregarPreview()
|
|
||||||
}
|
|
||||||
|
|
||||||
fecharPreview() {
|
|
||||||
if (!this.hasOverlayTarget || this.overlayTarget.hidden) return
|
|
||||||
|
|
||||||
this.overlayTarget.hidden = true
|
|
||||||
this.overlayTarget.classList.remove("flex")
|
|
||||||
this.overlayTarget.classList.add("hidden")
|
|
||||||
document.body.style.overflow = ""
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clique no fundo escuro fecha; clique DENTRO da caixa não — senão mexer no
|
|
||||||
// PDF fecharia a prévia sem querer.
|
|
||||||
fecharPorFora(event) {
|
|
||||||
if (this.hasCaixaPreviewTarget && this.caixaPreviewTarget.contains(event.target)) return
|
|
||||||
this.fecharPreview()
|
|
||||||
}
|
|
||||||
|
|
||||||
carregarPreview() {
|
carregarPreview() {
|
||||||
if (!this.hasPreviewTarget) return
|
if (!this.hasPreviewTarget) return
|
||||||
|
|
||||||
@@ -171,16 +145,25 @@ export default class extends Controller {
|
|||||||
this.previewDesatualizado = false
|
this.previewDesatualizado = false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Chamada depois de cada gravação. Com a prévia FECHADA não gera PDF nenhum —
|
// Chamada depois de cada gravação. Com o bloco FECHADO não gera PDF nenhum —
|
||||||
// só marca que o próximo "Ver prévia" precisa buscar de novo.
|
// 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.
|
||||||
atualizarPreview() {
|
atualizarPreview() {
|
||||||
if (!this.hasOverlayTarget || this.overlayTarget.hidden) {
|
if (!this.hasOverlayTarget || !this.overlayTarget.open) {
|
||||||
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
|
||||||
|
// ainda mostra a folha antiga, então recarregamos no momento da abertura.
|
||||||
|
aoAbrirPreview() {
|
||||||
|
if (this.hasOverlayTarget && this.overlayTarget.open && this.previewDesatualizado) {
|
||||||
|
this.carregarPreview()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ── Infra ───────────────────────────────────────────────────────────────
|
// ── Infra ───────────────────────────────────────────────────────────────
|
||||||
gravar(linhaId, campo, valor, input, botao, aoConcluir) {
|
gravar(linhaId, campo, valor, input, botao, aoConcluir) {
|
||||||
this.status("Salvando…")
|
this.status("Salvando…")
|
||||||
|
|||||||
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>
|
||||||
|
|
||||||
<div class="space-y-2 pt-1">
|
<div class="space-y-2 pt-1">
|
||||||
<%# Prévia sob demanda: o PDF só é gerado quando alguém pede para ver. %>
|
<%# A prévia NÃO tem botão aqui: ela é o bloco recolhível no topo da coluna
|
||||||
<button type="button" data-action="romaneio#abrirPreview"
|
ao lado (`_previa`), aberto pelo próprio navegador. Um segundo botão que
|
||||||
class="w-full min-h-[48px] flex items-center justify-center gap-2 bg-[#1a1a1a]
|
dependesse de JavaScript para abrir aquele bloco voltaria a ficar morto
|
||||||
hover:bg-[#242424] text-white border border-white/10 rounded-xl">
|
quando os assets falhassem — que é justamente o que se está evitando. %>
|
||||||
<%= icone :visualizar, cor: nil %> Ver prévia do PDF
|
|
||||||
</button>
|
|
||||||
<%= 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]
|
||||||
|
|||||||
@@ -55,7 +55,19 @@
|
|||||||
<div class="bg-[#111] border border-white/5 rounded-2xl p-5 space-y-4">
|
<div class="bg-[#111] border border-white/5 rounded-2xl p-5 space-y-4">
|
||||||
<h2 class="text-white font-semibold">Importar plano</h2>
|
<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,
|
<%= form_with url: admin_romaneios_path, method: :post, multipart: true,
|
||||||
|
authenticity_token: form_authenticity_token,
|
||||||
data: { turbo: false }, class: 'space-y-4' do %>
|
data: { turbo: false }, class: 'space-y-4' do %>
|
||||||
<div class="grid grid-cols-1 sm:grid-cols-3 gap-3">
|
<div class="grid grid-cols-1 sm:grid-cols-3 gap-3">
|
||||||
<div>
|
<div>
|
||||||
|
|||||||
@@ -1,12 +1,7 @@
|
|||||||
<%# 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 sobreposição aberta sob demanda.
|
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). %>
|
||||||
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"
|
<div class="space-y-5"
|
||||||
data-controller="romaneio"
|
data-controller="romaneio"
|
||||||
data-romaneio-url-value="<%= admin_romaneio_path(@romaneio) %>"
|
data-romaneio-url-value="<%= admin_romaneio_path(@romaneio) %>"
|
||||||
@@ -69,63 +64,22 @@
|
|||||||
<%= render 'operacao' %>
|
<%= render 'operacao' %>
|
||||||
<%= render 'avisos' %>
|
<%= render 'avisos' %>
|
||||||
|
|
||||||
<%# Duas zonas na tela: VEÍCULOS e a edição. A prévia saiu do fluxo e virou
|
<%# Duas zonas na tela: VEÍCULOS e a edição. A prévia é o bloco fechado no topo
|
||||||
sobreposição sob demanda (botão "Ver prévia") — antes o <iframe> era montado
|
da coluna de edição — o PDF só é gerado quando alguém o abre (antes o
|
||||||
em TODO carregamento, e cada montagem gerava um PDF no servidor só para
|
<iframe> era montado em TODO carregamento, gerando um PDF no servidor para
|
||||||
ficar num canto que o operador nem sempre olha. Agora o PDF só é gerado
|
ficar num canto que o operador nem sempre olha). %>
|
||||||
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="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>
|
||||||
|
|
||||||
<%# ── 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 %>
|
<% end %>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -115,29 +115,33 @@ agent-browser wait --load networkidle
|
|||||||
|
|
||||||
Na tela do romaneio:
|
Na tela do romaneio:
|
||||||
|
|
||||||
|
A prévia é um `<details>` nativo — abre e fecha **sem JavaScript**, de propósito
|
||||||
|
(quando os assets caem, ela precisa continuar funcionando):
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# 4a. O modal de prévia NÃO pode estar visível no carregamento:
|
# 4a. Fechada ao carregar, e o PDF ainda NÃO foi buscado (iframe é loading=lazy):
|
||||||
agent-browser eval "const o=document.querySelector('[data-romaneio-target=overlay]'); o?getComputedStyle(o).display:'sem overlay'"
|
agent-browser eval "(()=>{const d=document.querySelector('details[data-romaneio-target=overlay]'); return d ? 'aberto:'+d.open : 'PREVIA AUSENTE';})()"
|
||||||
# esperado: "none"
|
# esperado: "aberto:false"
|
||||||
|
|
||||||
# 4b. Abrir a prévia:
|
# 4b. Abrir e conferir que o iframe aponta para o PDF:
|
||||||
agent-browser snapshot -i | grep -i "prévia" # achar o botão "Ver prévia do PDF"
|
agent-browser eval "document.querySelector('details[data-romaneio-target=overlay] summary').click()"
|
||||||
agent-browser click @eY
|
agent-browser eval "(()=>{const d=document.querySelector('details[data-romaneio-target=overlay]'); const f=d.querySelector('iframe'); return 'aberto:'+d.open+' src:'+(f?.getAttribute('src')||'sem src');})()"
|
||||||
agent-browser eval "const o=document.querySelector('[data-romaneio-target=overlay]'); getComputedStyle(o).display + ' src:' + (document.querySelector('iframe[data-romaneio-target=preview]')?.src||'')"
|
# esperado: "aberto:true src:/admin/romaneios/<id>/pdf?veiculo=..."
|
||||||
# esperado: "flex src:https://.../pdf?veiculo=..."
|
|
||||||
|
|
||||||
# 4c. Fechar com Esc:
|
# 4c. Fechar de novo:
|
||||||
agent-browser press Escape
|
agent-browser eval "document.querySelector('details[data-romaneio-target=overlay] summary').click()"
|
||||||
agent-browser eval "getComputedStyle(document.querySelector('[data-romaneio-target=overlay]')).display"
|
|
||||||
# esperado: "none"
|
|
||||||
|
|
||||||
# 4d. PDF responde:
|
# 4d. O PDF responde e é PDF de verdade (não a página de login):
|
||||||
agent-browser eval "fetch(document.querySelector('a[href*=pdf]').href).then(r=>r.status)"
|
agent-browser eval "(async()=>{const r=await fetch(document.querySelector('details[data-romaneio-target=overlay] iframe').src); const b=await r.blob(); return r.status+' '+b.type+' '+Math.round(b.size/1024)+'KB';})()"
|
||||||
# esperado: 200
|
# esperado: "200 application/pdf <N>KB"
|
||||||
```
|
```
|
||||||
|
|
||||||
**PASSOU se**: 4a="none", 4b="flex" com src preenchido, 4c="none", 4d=200,
|
**PASSOU se**: 4a fechada, 4b abre com src do PDF, 4c fecha, 4d devolve
|
||||||
Teste 0 limpo. **NÃO** clicar em "Reimportar plano" (tem confirmação).
|
`application/pdf`, Teste 0 limpo. **NÃO** clicar em "Reimportar plano" (tem
|
||||||
|
confirmação).
|
||||||
|
|
||||||
|
⚠️ Se 4d devolver `text/html`, a sessão caiu no meio do teste — refaça o login e
|
||||||
|
repita, **não** registre como falha do PDF (já deu falso positivo assim).
|
||||||
|
|
||||||
### Caso 4e — Vínculo manual da operação (só leitura)
|
### Caso 4e — Vínculo manual da operação (só leitura)
|
||||||
|
|
||||||
@@ -210,6 +214,47 @@ agent-browser open https://teste.reemtransportes.com.br/auth/login
|
|||||||
**PASSOU se**: cai no dashboard do motorista, valores legíveis sem zoom,
|
**PASSOU se**: cai no dashboard do motorista, valores legíveis sem zoom,
|
||||||
Teste 0 limpo. Ao final: `agent-browser set viewport 1280 800`.
|
Teste 0 limpo. Ao final: `agent-browser set viewport 1280 800`.
|
||||||
|
|
||||||
|
## Caso 9 — Tempo de resposta (performance)
|
||||||
|
|
||||||
|
Mede o tempo real de cada tela e do PDF. Rode logado, com a rede parada (sem
|
||||||
|
outra aba baixando coisa), e compare com a linha de base.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Tempo das telas (TTFB | resposta completa | DOM pronto)
|
||||||
|
for u in /dashboard /dashboard/operacoes "/consolidacoes?visao=lista" /admin/romaneios /admin/romaneios/13; do
|
||||||
|
agent-browser open "https://teste.reemtransportes.com.br$u" >/dev/null
|
||||||
|
agent-browser eval "(()=>{const n=performance.getEntriesByType('navigation')[0]; return Math.round(n.responseStart-n.requestStart)+'|'+Math.round(n.responseEnd-n.requestStart)+'|'+Math.round(n.domContentLoadedEventEnd-n.startTime);})()"
|
||||||
|
done
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Tempo do PDF (o mais pesado do sistema). Com cache-buster, senão mede o cache.
|
||||||
|
agent-browser eval "(async()=>{const t0=performance.now(); const r=await fetch('/admin/romaneios/13/pdf?veiculo=GADE_002&cb='+Math.random()); const b=await r.blob(); return Math.round(performance.now()-t0)+'ms, '+Math.round(b.size/1024)+' KB';})()"
|
||||||
|
```
|
||||||
|
|
||||||
|
**Linha de base medida em 28/08/2026** (romaneio de 2.047 paradas / 72 veículos,
|
||||||
|
ambiente de teste, acesso pelo domínio com Cloudflare na frente):
|
||||||
|
|
||||||
|
| Alvo | TTFB | Resposta | DOM pronto |
|
||||||
|
|---|---|---|---|
|
||||||
|
| `/dashboard` | 339 ms | 340 ms | 493 ms |
|
||||||
|
| `/dashboard/operacoes` | 168 ms | 251 ms | 402 ms |
|
||||||
|
| `/consolidacoes?visao=lista` | 349 ms | 351 ms | 430 ms |
|
||||||
|
| `/admin/romaneios` | 49 ms | 50 ms | 127 ms |
|
||||||
|
| `/admin/romaneios/13` | 71 ms | 78 ms | 155 ms |
|
||||||
|
| **PDF de 1 veículo** | — | **228 ms** (84 KB) | — |
|
||||||
|
| **PDF de TODOS (72)** | — | **8.947 ms** (1,7 MB) | — |
|
||||||
|
|
||||||
|
**Orçamento — FALHOU se passar de**: 1 s de TTFB em qualquer tela; 1 s no PDF de
|
||||||
|
um veículo (é ele que a prévia carrega, tem que abrir na hora); 30 s no PDF
|
||||||
|
completo (acima disso o proxy corta a conexão e o operador recebe erro).
|
||||||
|
|
||||||
|
⚠️ **Ponto de atenção conhecido**: o PDF de todos os veículos leva ~9 s e segura
|
||||||
|
um processo do servidor esse tempo todo. Ainda dentro do aceitável para uma ação
|
||||||
|
manual e pontual, mas é o primeiro candidato a virar download em segundo plano se
|
||||||
|
o volume crescer ou se dois operadores baixarem ao mesmo tempo. **Não** compare
|
||||||
|
esse número com o do PDF de um veículo: são unidades diferentes (1 folha × 72).
|
||||||
|
|
||||||
## Relatório final (obrigatório)
|
## Relatório final (obrigatório)
|
||||||
|
|
||||||
Terminar a execução com uma tabela e a lista de falhas:
|
Terminar a execução com uma tabela e a lista de falhas:
|
||||||
@@ -225,6 +270,7 @@ Terminar a execução com uma tabela e a lista de falhas:
|
|||||||
| 6 Admin | … | … |
|
| 6 Admin | … | … |
|
||||||
| 7 Notificações | … | … |
|
| 7 Notificações | … | … |
|
||||||
| 8 PIN motorista | … | … |
|
| 8 PIN motorista | … | … |
|
||||||
|
| 9 Performance | … | tabela de tempos |
|
||||||
|
|
||||||
Depois da tabela, listar **somente as falhas**, cada uma com: URL, o que era
|
Depois da tabela, listar **somente as falhas**, cada uma com: URL, o que era
|
||||||
esperado, o que aconteceu (mensagem de erro/console) e o caminho do screenshot.
|
esperado, o que aconteceu (mensagem de erro/console) e o caminho do screenshot.
|
||||||
|
|||||||
Reference in New Issue
Block a user