, Correçoes UX
This commit is contained in:
@@ -5,10 +5,16 @@
|
|||||||
<div class="grid grid-cols-1 sm:grid-cols-[minmax(0,1fr)_minmax(0,280px)] gap-3">
|
<div class="grid grid-cols-1 sm:grid-cols-[minmax(0,1fr)_minmax(0,280px)] gap-3">
|
||||||
<div>
|
<div>
|
||||||
<label class="block text-sm font-medium text-gray-300 mb-1.5">Buscar nesta folha</label>
|
<label class="block text-sm font-medium text-gray-300 mb-1.5">Buscar nesta folha</label>
|
||||||
<div class="flex gap-2">
|
<%# `min-w-0` no input é o que impede rolagem lateral no celular: um <input>
|
||||||
|
tem largura intrínseca (~20 caracteres) e `flex-1` NÃO encolhe abaixo
|
||||||
|
dela, porque o `min-width` padrão de um item flex é `auto`. Com o botão
|
||||||
|
ao lado, o par estourava para 487px numa tela de 393px e levava a página
|
||||||
|
junto. O `flex-wrap` é a segunda rede: em tela muito estreita o botão
|
||||||
|
desce em vez de espremer o campo. %>
|
||||||
|
<div class="flex flex-wrap gap-2">
|
||||||
<input type="search" placeholder="NF, nome ou bairro/rua…"
|
<input type="search" placeholder="NF, nome ou bairro/rua…"
|
||||||
data-romaneio-target="busca" data-action="input->romaneio#filtrar"
|
data-romaneio-target="busca" data-action="input->romaneio#filtrar"
|
||||||
class="flex-1 bg-[#1a1a1a] border border-white/10 rounded-xl px-3 py-3 text-white
|
class="flex-1 min-w-0 bg-[#1a1a1a] border border-white/10 rounded-xl px-3 py-3 text-white
|
||||||
placeholder-gray-600 focus:outline-none focus:border-orange-500 min-h-[48px]">
|
placeholder-gray-600 focus:outline-none focus:border-orange-500 min-h-[48px]">
|
||||||
<%# Rótulo que descreve o que o controle faz (diretriz 2): limpa a BUSCA,
|
<%# Rótulo que descreve o que o controle faz (diretriz 2): limpa a BUSCA,
|
||||||
não o romaneio. %>
|
não o romaneio. %>
|
||||||
|
|||||||
@@ -4,10 +4,29 @@
|
|||||||
paradas cada um tem, que é justamente a conferência que ele faz antes de
|
paradas cada um tem, que é justamente a conferência que ele faz antes de
|
||||||
mandar imprimir.
|
mandar imprimir.
|
||||||
|
|
||||||
Adapta ao dispositivo (diretriz 3): no celular vira faixa horizontal, porque
|
── NO CELULAR É UM BLOCO RECOLHIDO (diretriz 3) ───────────────────────────
|
||||||
72 itens empilhados empurrariam a tabela para fora da primeira tela; no
|
Aberto, este painel tem os 72 carros mais o campo de motorista e três botões:
|
||||||
desktop é coluna com rolagem própria, do lado da tabela. %>
|
media 1.160px de altura e empurrava a EDIÇÃO — busca, campo PLANO e a tabela
|
||||||
<div class="bg-[#111] border border-white/5 rounded-2xl p-4 space-y-3">
|
de paradas — para 1.330px e 1.533px de rolagem, ou seja, duas telas abaixo do
|
||||||
|
que o operador foi fazer ali. Medido em 28/08/2026 no iPhone.
|
||||||
|
|
||||||
|
Recolhido, o <summary> mostra o que importa de relance (carro atual e quantas
|
||||||
|
paradas) e a edição sobe para a primeira tela. Um toque abre a lista inteira.
|
||||||
|
|
||||||
|
NO DESKTOP NADA MUDA: o <summary> some (`lg:hidden`) e o conteúdo é forçado a
|
||||||
|
aparecer com `lg:block`, que vence o "esconder" nativo do <details> fechado —
|
||||||
|
continua a coluna sempre aberta ao lado da tabela. %>
|
||||||
|
<details class="bg-[#111] border border-white/5 rounded-2xl overflow-hidden">
|
||||||
|
<summary class="lg:hidden cursor-pointer list-none min-h-[56px] px-4 py-3 flex items-center gap-3
|
||||||
|
text-white hover:bg-white/5">
|
||||||
|
<span class="font-semibold truncate"><%= @veiculo %></span>
|
||||||
|
<span class="text-xs px-2 py-0.5 rounded-lg bg-white/5 text-gray-400 shrink-0">
|
||||||
|
<%= @romaneio.contagem_por_veiculo[@veiculo].to_i %> paradas
|
||||||
|
</span>
|
||||||
|
<span class="ml-auto text-xs text-orange-400 shrink-0">trocar veículo</span>
|
||||||
|
</summary>
|
||||||
|
|
||||||
|
<div class="lg:block p-4 space-y-3">
|
||||||
<div class="flex items-center justify-between">
|
<div class="flex items-center justify-between">
|
||||||
<h2 class="text-white font-semibold text-sm">Veículos</h2>
|
<h2 class="text-white font-semibold text-sm">Veículos</h2>
|
||||||
<span class="text-xs text-gray-500"><%= @veiculos.size %> no plano</span>
|
<span class="text-xs text-gray-500"><%= @veiculos.size %> no plano</span>
|
||||||
@@ -109,3 +128,4 @@
|
|||||||
<% end %>
|
<% end %>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</details>
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
<%# app/views/consolidacoes/arquivadas.html.erb %>
|
<%# app/views/consolidacoes/arquivadas.html.erb %>
|
||||||
<% content_for :title, 'Consolidações arquivadas' %>
|
<% content_for :title, 'Consolidações arquivadas' %>
|
||||||
|
|
||||||
<div class="flex items-center justify-between mb-6">
|
<%# flex-wrap pelo mesmo motivo do cabeçalho de consolidacoes/index: título longo
|
||||||
|
mais botão não cabem na largura de um celular e esticariam a página. %>
|
||||||
|
<div class="flex flex-wrap items-center justify-between gap-3 mb-6">
|
||||||
<h1 class="text-2xl font-bold text-white flex items-center gap-2"><%= icone :arquivadas, espaco: false %> Consolidações arquivadas</h1>
|
<h1 class="text-2xl font-bold text-white flex items-center gap-2"><%= icone :arquivadas, espaco: false %> Consolidações arquivadas</h1>
|
||||||
<%= link_to rotulo(:voltar, 'Voltar às ativas', cor: nil), consolidacoes_path,
|
<%= link_to rotulo(:voltar, 'Voltar às ativas', cor: nil), consolidacoes_path,
|
||||||
class: 'text-gray-400 hover:text-white border border-[#2a2a2a] px-4 py-3 rounded-lg min-h-[48px] flex items-center' %>
|
class: 'text-gray-400 hover:text-white border border-[#2a2a2a] px-4 py-3 rounded-lg min-h-[48px] flex items-center' %>
|
||||||
|
|||||||
@@ -3,9 +3,14 @@
|
|||||||
|
|
||||||
<div data-controller="nota-avulsa"
|
<div data-controller="nota-avulsa"
|
||||||
data-nota-avulsa-buscar-url-value="<%= buscar_nf_consolidacoes_path %>">
|
data-nota-avulsa-buscar-url-value="<%= buscar_nf_consolidacoes_path %>">
|
||||||
<div class="flex items-center justify-between mb-6">
|
<%# `flex-wrap` nos DOIS níveis: no celular o título mais os três botões
|
||||||
|
("Arquivadas", "Nota avulsa", "+ Nova Consolidação") somam mais de 450px e
|
||||||
|
não cabem nos 393px da tela. Sem a quebra, eles esticavam a PÁGINA INTEIRA
|
||||||
|
para 732px e tudo — incluindo a lista de consolidações — passava a exigir
|
||||||
|
rolagem lateral. Medido em 28/08/2026 no iPhone. %>
|
||||||
|
<div class="flex flex-wrap items-center justify-between gap-3 mb-6">
|
||||||
<h1 class="text-2xl font-bold text-white flex items-center gap-2"><%= icone :consolidacoes, espaco: false %> Consolidações</h1>
|
<h1 class="text-2xl font-bold text-white flex items-center gap-2"><%= icone :consolidacoes, espaco: false %> Consolidações</h1>
|
||||||
<div class="flex gap-2">
|
<div class="flex flex-wrap gap-2">
|
||||||
<%= link_to rotulo(:arquivadas, 'Arquivadas', cor: nil), arquivadas_consolidacoes_path,
|
<%= link_to rotulo(:arquivadas, 'Arquivadas', cor: nil), arquivadas_consolidacoes_path,
|
||||||
class: 'text-gray-400 hover:text-white border border-[#2a2a2a] px-4 py-3 rounded-lg min-h-[48px] flex items-center transition-colors' %>
|
class: 'text-gray-400 hover:text-white border border-[#2a2a2a] px-4 py-3 rounded-lg min-h-[48px] flex items-center transition-colors' %>
|
||||||
<% if policy(Consolidacao).create? %>
|
<% if policy(Consolidacao).create? %>
|
||||||
|
|||||||
@@ -584,7 +584,11 @@
|
|||||||
info.textContent = de + '–' + ate + ' de ' + linhas.length + ' ' + rotulo;
|
info.textContent = de + '–' + ate + ' de ' + linhas.length + ' ' + rotulo;
|
||||||
abas.querySelectorAll('button').forEach(function (b) {
|
abas.querySelectorAll('button').forEach(function (b) {
|
||||||
const ativa = Number(b.dataset.pagina) === p;
|
const ativa = Number(b.dataset.pagina) === p;
|
||||||
b.className = 'px-3 py-1.5 rounded-lg text-xs font-semibold transition-colors ' +
|
// min-w/min-h 44px: são os alvos de toque mais apertados do sistema
|
||||||
|
// (medidos 63 abaixo de 44px no celular em 28/08/2026) e ficam lado a
|
||||||
|
// lado, então errar o número da página é fácil com o dedo em movimento.
|
||||||
|
b.className = 'min-w-[44px] min-h-[44px] px-3 py-1.5 rounded-lg text-xs font-semibold ' +
|
||||||
|
'inline-flex items-center justify-center transition-colors ' +
|
||||||
(ativa ? 'bg-[#f97316] text-white'
|
(ativa ? 'bg-[#f97316] text-white'
|
||||||
: 'bg-[#0a0a0a] text-gray-400 border border-white/5 hover:text-white');
|
: 'bg-[#0a0a0a] text-gray-400 border border-white/5 hover:text-white');
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -6,19 +6,31 @@
|
|||||||
class="fixed inset-0 bg-black/70 z-30 hidden md:hidden"
|
class="fixed inset-0 bg-black/70 z-30 hidden md:hidden"
|
||||||
onclick="toggleSidebar()"></div>
|
onclick="toggleSidebar()"></div>
|
||||||
|
|
||||||
<%# Sidebar %>
|
<%# Sidebar
|
||||||
|
`flex flex-col` NÃO é decoração: é o que faz o menu ROLAR. O <nav> do meio
|
||||||
|
pede `flex-1 overflow-y-auto`, e `flex-1` só tem efeito dentro de um flex
|
||||||
|
container — sem isto o <nav> crescia com o conteúdo, nunca ganhava altura
|
||||||
|
limitada, o `overflow-y-auto` nunca ativava e a lista simplesmente
|
||||||
|
transbordava o painel sem barra de rolagem.
|
||||||
|
|
||||||
|
Caso real (28/08/2026, iPhone 393×852): 1.194px de menu dentro de 852px de
|
||||||
|
tela, com o último item terminando 324px ABAIXO da borda. Contatos, Grupos,
|
||||||
|
Envios, Eventos, Variáveis, WhatsApp e o **Sair** ficavam inalcançáveis no
|
||||||
|
celular — e no notebook de tela baixa também. %>
|
||||||
<aside id="sidebar"
|
<aside id="sidebar"
|
||||||
class="fixed top-0 left-0 h-full w-64 bg-[#111111] border-r border-[#2a2a2a] z-40
|
class="fixed top-0 left-0 h-full w-64 bg-[#111111] border-r border-[#2a2a2a] z-40
|
||||||
|
flex flex-col
|
||||||
transform -translate-x-full md:translate-x-0 transition-transform duration-300">
|
transform -translate-x-full md:translate-x-0 transition-transform duration-300">
|
||||||
|
|
||||||
<%# Logo / Marca %>
|
<%# Logo / Marca — `shrink-0`: num flex-col, um bloco de altura fixa ainda é
|
||||||
<div class="px-4 py-5 border-b border-[#2a2a2a]">
|
espremido quando o irmão rolável é grande demais. %>
|
||||||
|
<div class="px-4 py-5 border-b border-[#2a2a2a] shrink-0">
|
||||||
<%= image_tag '/logo-reem.png', alt: 'Reem Transporte',
|
<%= image_tag '/logo-reem.png', alt: 'Reem Transporte',
|
||||||
class: 'h-14 w-auto mx-auto object-contain' %>
|
class: 'h-14 w-auto mx-auto object-contain' %>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<%# Usuário logado %>
|
<%# Usuário logado %>
|
||||||
<div class="px-6 py-4 border-b border-[#2a2a2a]">
|
<div class="px-6 py-4 border-b border-[#2a2a2a] shrink-0">
|
||||||
<p class="text-xs text-gray-500 mb-1">Logado como</p>
|
<p class="text-xs text-gray-500 mb-1">Logado como</p>
|
||||||
<p class="text-white font-semibold text-sm truncate"><%= current_user.nome_display %></p>
|
<p class="text-white font-semibold text-sm truncate"><%= current_user.nome_display %></p>
|
||||||
<span class="inline-block mt-1 px-2 py-0.5 text-xs rounded-full
|
<span class="inline-block mt-1 px-2 py-0.5 text-xs rounded-full
|
||||||
@@ -114,7 +126,9 @@
|
|||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<%# Footer da sidebar — sair %>
|
<%# Footer da sidebar — sair %>
|
||||||
<div class="px-6 py-4 border-t border-[#2a2a2a]">
|
<%# Rodapé com o "Sair": shrink-0 para ele ficar ancorado embaixo, sempre
|
||||||
|
visível, em vez de rolar junto com a lista. %>
|
||||||
|
<div class="px-6 py-4 border-t border-[#2a2a2a] shrink-0">
|
||||||
<%= link_to destroy_user_session_path, data: { turbo_method: :delete, turbo_confirm: 'Deseja sair?' },
|
<%= link_to destroy_user_session_path, data: { turbo_method: :delete, turbo_confirm: 'Deseja sair?' },
|
||||||
class: "flex items-center gap-2 text-gray-400 hover:text-red-400 text-sm transition-colors" do %>
|
class: "flex items-center gap-2 text-gray-400 hover:text-red-400 text-sm transition-colors" do %>
|
||||||
<%= icone :sair, cor: nil, tamanho: 'w-5 h-5' %> Sair
|
<%= icone :sair, cor: nil, tamanho: 'w-5 h-5' %> Sair
|
||||||
|
|||||||
@@ -301,6 +301,64 @@ manual e pontual, mas é o primeiro candidato a virar download em segundo plano
|
|||||||
o volume crescer ou se dois operadores baixarem ao mesmo tempo. **Não** compare
|
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).
|
esse número com o do PDF de um veículo: são unidades diferentes (1 folha × 72).
|
||||||
|
|
||||||
|
## Caso 10 — Mobile: quebras visuais (varredura)
|
||||||
|
|
||||||
|
Roda em **todas** as telas do roteiro com `agent-browser set device "iPhone 16"`
|
||||||
|
(393×852), logado. São três medições objetivas; anote o número, não só passou/falhou.
|
||||||
|
|
||||||
|
### 10a — Rolagem horizontal
|
||||||
|
|
||||||
|
Nenhuma tela pode ter rolagem lateral no celular (diretriz 3: nada que exija zoom).
|
||||||
|
|
||||||
|
```bash
|
||||||
|
agent-browser set device "iPhone 16"
|
||||||
|
for u in /dashboard /dashboard/operacoes "/consolidacoes?visao=lista" "/consolidacoes?visao=motoristas" /admin/romaneios /admin/romaneios/13; do
|
||||||
|
agent-browser open "https://teste.reemtransportes.com.br$u" >/dev/null
|
||||||
|
agent-browser eval "document.documentElement.scrollWidth+'/'+document.documentElement.clientWidth"
|
||||||
|
done
|
||||||
|
```
|
||||||
|
|
||||||
|
**PASSOU se**: os dois números são iguais (ex.: `393/393`) em todas.
|
||||||
|
Para achar o culpado quando falhar:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
agent-browser eval "(()=>{const vw=document.documentElement.clientWidth; return [...document.querySelectorAll('*')].filter(e=>{const b=e.getBoundingClientRect(); return b.right>vw+2 && b.width>40 && getComputedStyle(e).position!=='fixed';}).slice(0,5).map(e=>e.tagName+'.'+String(e.className).split(' ').slice(0,2).join('.')+' right='+Math.round(e.getBoundingClientRect().right)).join(' || ');})()"
|
||||||
|
```
|
||||||
|
|
||||||
|
> ⚠️ Causa recorrente: contêiner `flex` **sem `flex-wrap`** com botões que não cabem,
|
||||||
|
> ou `<input class="flex-1">` **sem `min-w-0`** (input não encolhe abaixo da largura
|
||||||
|
> intrínseca). Foram as duas causas de 28/08/2026.
|
||||||
|
|
||||||
|
### 10b — Menu inteiro alcançável
|
||||||
|
|
||||||
|
Todo item do menu tem que ser alcançável rolando — inclusive o **Sair**, o último.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
agent-browser open "https://teste.reemtransportes.com.br/dashboard"
|
||||||
|
agent-browser snapshot -i | grep -i "menu" # pegar o ref do botão "Abrir menu"
|
||||||
|
agent-browser click @eN
|
||||||
|
agent-browser eval "(()=>{const nav=document.querySelector('#sidebar nav'); if(!nav) return 'NAV AUSENTE'; const ult=[...document.querySelectorAll('#sidebar a')].at(-1); return 'nav rola='+(nav.scrollHeight>nav.clientHeight)+' overflowY='+getComputedStyle(nav).overflowY+' | ultimo item: '+ult.textContent.trim()+' | fora da tela por '+Math.max(0, Math.round(ult.getBoundingClientRect().bottom-window.innerHeight))+'px';})()"
|
||||||
|
```
|
||||||
|
|
||||||
|
**PASSOU se**: `overflowY=auto` e "fora da tela por 0px" (o item pode estar abaixo
|
||||||
|
da dobra, desde que o `<nav>` role até ele).
|
||||||
|
**FALHOU se**: `overflowY=visible` ou o último item aparece fora por N px — foi o
|
||||||
|
bug de 28/08/2026, em que 7 itens (incluindo Sair) eram inalcançáveis no celular.
|
||||||
|
|
||||||
|
> O que segura isso é o `flex flex-col` no `<aside id="sidebar">`: sem ele o
|
||||||
|
> `flex-1 overflow-y-auto` do `<nav>` não tem efeito nenhum.
|
||||||
|
|
||||||
|
### 10c — Alvos de toque
|
||||||
|
|
||||||
|
```bash
|
||||||
|
agent-browser eval "(()=>{const p=[...document.querySelectorAll('a,button,input,select,summary')].filter(e=>{const b=e.getBoundingClientRect(); return b.height>0 && b.height<44;}); return p.length+' alvos <44px';})()"
|
||||||
|
```
|
||||||
|
|
||||||
|
**Linha de base após as correções de 28/08/2026** (iPhone 16): dashboard tinha
|
||||||
|
**63** antes do ajuste da paginação; as demais telas ficam entre 3 e 5. Um número
|
||||||
|
que **cresce** entre execuções é regressão — vale investigar o que foi
|
||||||
|
adicionado, não apenas registrar.
|
||||||
|
|
||||||
## 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:
|
||||||
@@ -317,6 +375,7 @@ Terminar a execução com uma tabela e a lista de falhas:
|
|||||||
| 7 Notificações | … | … |
|
| 7 Notificações | … | … |
|
||||||
| 8 PIN motorista | … | … |
|
| 8 PIN motorista | … | … |
|
||||||
| 9 Performance | … | tabela de tempos |
|
| 9 Performance | … | tabela de tempos |
|
||||||
|
| 10 Mobile (10a/b/c) | … | larguras, menu, alvos |
|
||||||
|
|
||||||
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