From 74dc16e508e5bdc67b394c8e8fec83617652b374253d84e713f0d148dd2f12d8 Mon Sep 17 00:00:00 2001 From: victor Date: Fri, 28 Aug 2026 17:44:42 -0300 Subject: [PATCH] =?UTF-8?q?,=20Corre=C3=A7oes=20na=20busca=20da=20API=20pa?= =?UTF-8?q?ra=20ver=20os=20ultimos=20planos?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/controllers/admin/romaneios_controller.rb | 45 ++++++++++- app/views/admin/romaneios/index.html.erb | 8 ++ app/views/layouts/application.html.erb | 76 +++++++++++++++++++ 3 files changed, 126 insertions(+), 3 deletions(-) diff --git a/app/controllers/admin/romaneios_controller.rb b/app/controllers/admin/romaneios_controller.rb index 5c1253c..255b996 100644 --- a/app/controllers/admin/romaneios_controller.rb +++ b/app/controllers/admin/romaneios_controller.rb @@ -6,10 +6,26 @@ # # As bases externas (espelho de rastreio e gade_entregas_*) continuam SOMENTE # LEITURA — o que gravamos são as tabelas `romaneio*`, nossas. +# FileUtils chegava aqui de carona no que o Rails carrega. Explícito porque o +# upload do logo depende dele: sem o require, o dia em que o Rails parar de +# carregá-lo o erro é um NameError no meio da gravação. +require 'fileutils' + class Admin::RomaneiosController < ApplicationController include Auditavel - before_action :carregar_romaneio, except: %i[index create importar_planilha] + # ⚠️ TODA ACTION DE COLEÇÃO PRECISA ENTRAR NESTE `except`. `carregar_romaneio` + # faz `Romaneio.find(params[:id])`, e numa rota de coleção não existe `:id` — + # o find levanta RecordNotFound, que o Rails traduz em **404**. Não é erro de + # rota: o roteador casa certinho e a action simplesmente nunca roda. + # + # Foi o bug de 28/08/2026: `atualizar_logo` foi adicionada ao `collection` do + # routes.rb mas esquecida aqui, e clicar em "Trocar logo" devolvia + # `POST /admin/romaneios/atualizar_logo 404 (Not Found)`. Como o 404 parece + # "rota errada", a busca foi para todo lado menos para cá — e o sintoma que o + # operador via era outro ("subi o logo e o PDF não mudou"), porque o upload + # nunca chegava a acontecer. + before_action :carregar_romaneio, except: %i[index create importar_planilha atualizar_logo] def index authorize Romaneio @@ -129,6 +145,14 @@ class Admin::RomaneiosController < ApplicationController arquivo = params[:logo] return redirect_to(admin_romaneios_path, alert: 'Escolha o arquivo do logo (PNG ou JPG).') if arquivo.blank? + # `params[:logo]` só é um arquivo se o
for multipart. Sem isto, um + # form mal montado manda uma String e a linha seguinte estoura com + # NoMethodError — página 500 em vez de "escolha o arquivo". + unless arquivo.respond_to?(:read) && arquivo.respond_to?(:size) + return redirect_to(admin_romaneios_path, + alert: 'O arquivo não chegou. Escolha o logo e envie de novo.') + end + if arquivo.size > LOGO_TAMANHO_MAX return redirect_to(admin_romaneios_path, alert: 'Logo acima de 2 MB. Reduza a imagem antes de enviar.') @@ -157,8 +181,23 @@ class Admin::RomaneiosController < ApplicationController # mostrar "Logo: LOGOGADE.png" como o operador reconhece. nome = "logo-#{Time.current.strftime('%Y%m%d%H%M%S')}#{extensao}" destino = Pdf::RomaneioPdf.pasta_logos - FileUtils.mkdir_p(destino) - File.binwrite(destino.join(nome), conteudo) + + # ⚠️ A GRAVAÇÃO PODE FALHAR, E FALHAR AQUI NÃO É "bug do Rails": `storage/` + # mora no share SMB do NAS, onde permissão de pasta se perde na cópia (o + # mesmo motivo que fez o bin/docker-boot ser chamado com `bash` explícito). + # Sem este rescue, um EACCES vira a página 500 crua na cara do operador, sem + # dizer o que houve nem o que fazer — foi o que aconteceu em 28/08/2026 ao + # clicar em "Trocar logo". + begin + FileUtils.mkdir_p(destino) + File.binwrite(destino.join(nome), conteudo) + rescue SystemCallError => e + Rails.logger.error("[romaneio] falha ao gravar logo em #{destino}: #{e.class}: #{e.message}") + return redirect_to(admin_romaneios_path, + alert: "Não consegui gravar o logo em #{destino} (#{e.class}). " \ + 'A pasta existe e o container tem permissão de escrita nela? ' \ + 'O erro completo está no log do container.') + end config = Configuracao.find_or_initialize_by(chave: 'romaneio_logo') config.update!(valor: nome, descricao: arquivo.original_filename.to_s.first(120), diff --git a/app/views/admin/romaneios/index.html.erb b/app/views/admin/romaneios/index.html.erb index d9150a5..c130ded 100644 --- a/app/views/admin/romaneios/index.html.erb +++ b/app/views/admin/romaneios/index.html.erb @@ -24,7 +24,15 @@ Fica ANTES da importação porque é configuração que vale para todos os romaneios, não escolha por plano: quem troca de contratante troca uma vez. %>
+ <%# `authenticity_token: form_authenticity_token` pelo mesmo motivo do + formulário de importação logo abaixo (o comentário longo está lá): com + `load_defaults 7.1` o Rails usa token POR FORMULÁRIO, e qualquer + descasamento entre o token embutido e a action recebida devolve "A ação + foi recusada por uma verificação de segurança". O helper sem argumentos + devolve o token GLOBAL, que vale para qualquer action da sessão — este + form era o único da tela sem ele. %> <%= form_with url: atualizar_logo_admin_romaneios_path, method: :post, multipart: true, + authenticity_token: form_authenticity_token, data: { turbo: false }, class: 'flex flex-col sm:flex-row sm:items-end gap-3' do %>
diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index 0eef752..4761e6c 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -47,6 +47,82 @@ ::-webkit-scrollbar { width: 6px; } ::-webkit-scrollbar-track { background: #0a0a0a; } ::-webkit-scrollbar-thumb { background: #f97316; border-radius: 3px; } + /* ── Checkbox do sistema ──────────────────────────────────────────────── + O checkbox nativo com `accent-color` continua sendo o do sistema + operacional: quadrado duro, cantos retos e um azul-acinzentado que aparece + no hover e no disabled. No meio de uma tela toda de cantos 12px e laranja + #f97316 ele é o único elemento que denuncia "formulário cru". + + Vale por SELETOR DE ELEMENTO, não por classe, de propósito: são ~20 + checkboxes em 17 telas e o site inteiro precisa ficar igual — inclusive os + que forem escritos depois, que não vão lembrar de uma classe nova. Não há + nada para aplicar em cada tela; já está aplicado. + + `:not(.sr-only)` protege o OUTRO controle que também é : os interruptores (o padrão `sr-only peer`, em que o + checkbox fica invisível e quem desenha é o irmão). Estilizar aqueles faria + aparecer uma caixinha em cima do interruptor. + + `min-width/min-height` em vez de `width/height` porque o Tailwind vem de + CDN e injeta o `w-4 h-4` de cada tela em ordem imprevisível: `min-*` vence + o `width` menor sem depender de quem carregou primeiro, então nenhum + checkbox encolhe. */ + input[type="checkbox"]:not(.sr-only) { + -webkit-appearance: none; + appearance: none; + min-width: 1.25rem; + min-height: 1.25rem; + flex-shrink: 0; + border: 1.5px solid rgba(255, 255, 255, .2); + border-radius: .375rem; + background-color: #0a0a0a; + cursor: pointer; + display: inline-grid; + place-content: center; + vertical-align: middle; + transition: background-color .15s ease, border-color .15s ease; + } + input[type="checkbox"]:not(.sr-only):hover { border-color: #f97316; } + /* :focus-visible e não :focus — o anel só aparece na navegação por teclado, + não quando o dedo/mouse acabou de marcar. */ + input[type="checkbox"]:not(.sr-only):focus-visible { + outline: 2px solid #f97316; + outline-offset: 2px; + } + input[type="checkbox"]:not(.sr-only):checked, + input[type="checkbox"]:not(.sr-only):indeterminate { + background-color: #f97316; + border-color: #f97316; + } + /* A marca é clip-path e não imagem: nada para carregar, escala sem borrar e + não depende de img-src do CSP. */ + input[type="checkbox"]:not(.sr-only):checked::before { + content: ''; + width: .75rem; + height: .75rem; + background-color: #fff; + clip-path: polygon(14% 44%, 0 65%, 50% 100%, 100% 16%, 80% 0%, 43% 62%); + } + /* Estado "algumas marcadas" (a seleção em massa das consolidações usa). */ + input[type="checkbox"]:not(.sr-only):indeterminate::before { + content: ''; + width: .625rem; + height: 2px; + border-radius: 1px; + background-color: #fff; + } + input[type="checkbox"]:not(.sr-only):disabled { + opacity: .4; + cursor: not-allowed; + border-color: rgba(255, 255, 255, .12); + } + /* O roxo da tela de Validar é semântico (entrega extraordinária), não + decoração — continua roxo, com a mesma forma dos outros. */ + input[type="checkbox"].accent-purple-500:not(.sr-only):checked { + background-color: #9333ea; + border-color: #9333ea; + } + /* Loading spinner laranja */ .spinner { border: 3px solid #1a1a1a;