, Correçoes na busca da API para ver os ultimos planos
This commit is contained in:
@@ -6,10 +6,26 @@
|
|||||||
#
|
#
|
||||||
# As bases externas (espelho de rastreio e gade_entregas_*) continuam SOMENTE
|
# As bases externas (espelho de rastreio e gade_entregas_*) continuam SOMENTE
|
||||||
# LEITURA — o que gravamos são as tabelas `romaneio*`, nossas.
|
# 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
|
class Admin::RomaneiosController < ApplicationController
|
||||||
include Auditavel
|
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
|
def index
|
||||||
authorize Romaneio
|
authorize Romaneio
|
||||||
@@ -129,6 +145,14 @@ class Admin::RomaneiosController < ApplicationController
|
|||||||
arquivo = params[:logo]
|
arquivo = params[:logo]
|
||||||
return redirect_to(admin_romaneios_path, alert: 'Escolha o arquivo do logo (PNG ou JPG).') if arquivo.blank?
|
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 <form> 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
|
if arquivo.size > LOGO_TAMANHO_MAX
|
||||||
return redirect_to(admin_romaneios_path,
|
return redirect_to(admin_romaneios_path,
|
||||||
alert: 'Logo acima de 2 MB. Reduza a imagem antes de enviar.')
|
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.
|
# mostrar "Logo: LOGOGADE.png" como o operador reconhece.
|
||||||
nome = "logo-#{Time.current.strftime('%Y%m%d%H%M%S')}#{extensao}"
|
nome = "logo-#{Time.current.strftime('%Y%m%d%H%M%S')}#{extensao}"
|
||||||
destino = Pdf::RomaneioPdf.pasta_logos
|
destino = Pdf::RomaneioPdf.pasta_logos
|
||||||
|
|
||||||
|
# ⚠️ 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)
|
FileUtils.mkdir_p(destino)
|
||||||
File.binwrite(destino.join(nome), conteudo)
|
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 = Configuracao.find_or_initialize_by(chave: 'romaneio_logo')
|
||||||
config.update!(valor: nome, descricao: arquivo.original_filename.to_s.first(120),
|
config.update!(valor: nome, descricao: arquivo.original_filename.to_s.first(120),
|
||||||
|
|||||||
@@ -24,7 +24,15 @@
|
|||||||
Fica ANTES da importação porque é configuração que vale para todos os
|
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. %>
|
romaneios, não escolha por plano: quem troca de contratante troca uma vez. %>
|
||||||
<div class="bg-[#111] border border-white/5 rounded-2xl p-5">
|
<div class="bg-[#111] border border-white/5 rounded-2xl p-5">
|
||||||
|
<%# `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,
|
<%= form_with url: atualizar_logo_admin_romaneios_path, method: :post, multipart: true,
|
||||||
|
authenticity_token: form_authenticity_token,
|
||||||
data: { turbo: false },
|
data: { turbo: false },
|
||||||
class: 'flex flex-col sm:flex-row sm:items-end gap-3' do %>
|
class: 'flex flex-col sm:flex-row sm:items-end gap-3' do %>
|
||||||
<div class="flex-1">
|
<div class="flex-1">
|
||||||
|
|||||||
@@ -47,6 +47,82 @@
|
|||||||
::-webkit-scrollbar { width: 6px; }
|
::-webkit-scrollbar { width: 6px; }
|
||||||
::-webkit-scrollbar-track { background: #0a0a0a; }
|
::-webkit-scrollbar-track { background: #0a0a0a; }
|
||||||
::-webkit-scrollbar-thumb { background: #f97316; border-radius: 3px; }
|
::-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 é <input
|
||||||
|
type=checkbox>: 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 */
|
/* Loading spinner laranja */
|
||||||
.spinner {
|
.spinner {
|
||||||
border: 3px solid #1a1a1a;
|
border: 3px solid #1a1a1a;
|
||||||
|
|||||||
Reference in New Issue
Block a user