, Correçoes UX consolidaçoes e de backa-end

This commit is contained in:
2026-08-28 16:38:51 -03:00
parent b6fe6f796f
commit bfa474262f
7 changed files with 290 additions and 40 deletions

View File

@@ -1,40 +1,111 @@
<%# app/views/consolidacoes/_lista.html.erb — visão por CONSOLIDAÇÃO %>
<% pode_arquivar = policy(Consolidacao).arquivar? %>
<% if @consolidacoes.any? %>
<%# Em celular/monitor pequeno a lista vira carrossel de 10 em 10; em tela
grande aparece inteira (ver carrossel_controller.js). %>
<div data-controller="carrossel" data-carrossel-tamanho-value="10" class="relative sm:px-16">
<div class="space-y-3">
<% @consolidacoes.each do |c| %>
<%= link_to consolidacao_path(c),
data: { carrossel_target: 'item' },
class: 'block bg-[#1a1a1a] border border-[#2a2a2a] hover:border-orange-500 rounded-xl p-5 transition-colors' do %>
<div class="flex flex-col md:flex-row md:items-center md:justify-between gap-3">
<div>
<div class="flex items-center gap-3 mb-1">
<h3 class="text-white font-bold text-lg"><%= c.nome %></h3>
<%= badge_status(c.status) %>
<% if c.finalizada? %><%= badge_pagamento(c.status_pagamento) %><% end %>
</div>
<p class="text-gray-400 text-sm">
<%= icone :calendario %> <%= l c.data_inicio, format: :short %> → <%= l c.data_fim, format: :short %>
· <%= icone :pessoa %> <%= c.consolidacao_motoristas.count %> motorista(s)
· Criada por <%= c.criador.nome_display %>
</p>
<% if c.operacoes.present? %>
<p class="text-gray-500 text-xs mt-1"><%= icone :pasta %> <%= c.operacoes_label.join(' · ') %></p>
<% end %>
</div>
<div class="text-right">
<p class="text-orange-500 font-black text-2xl"><%= moeda(c.valor_total) %></p>
<p class="text-gray-500 text-xs"><%= c.consolidacao_entregas.count %> entregas classificadas</p>
</div>
</div>
<%# ── Seleção em massa ──────────────────────────────────────────────────
Arquivar uma a uma custava três passos por consolidação (abrir, arquivar,
voltar para o filtro) — inviável para fechar um mês inteiro de uma vez.
A linha aparece SEMPRE (não só depois de marcar algo): um controle que só
existe depois de descobrir que ele existe não é descoberto. Ela só some
para quem não tem `consolidacao.arquivar`.
O `data-controller` envolve a linha E a lista porque os checkboxes dos
cards são targets deste mesmo controller. %>
<div data-controller="selecao-lote">
<% if pode_arquivar %>
<%= form_with url: arquivar_em_lote_consolidacoes_path, method: :post,
id: 'form-arquivar-lote',
data: { turbo_confirm: 'Arquivar as consolidações marcadas? Elas saem da lista, mas dá para restaurar em "Arquivadas".' },
class: 'flex flex-wrap items-center gap-3 bg-[#1a1a1a] border border-[#2a2a2a] rounded-xl px-4 py-3 mb-4' do %>
<label class="flex items-center gap-2 cursor-pointer min-h-[48px] text-sm text-gray-300 hover:text-white">
<%# Sem `name`: este checkbox comanda os outros, não é dado do form. %>
<input type="checkbox" class="w-5 h-5 accent-orange-500"
data-selecao-lote-target="todos"
data-action="change->selecao-lote#alternarTodos">
Selecionar todas
</label>
<%# Texto inicial NEUTRO de propósito: com o JS fora do ar ele nunca é
substituído, e "Nenhuma selecionada" fixo na tela seria mentira. %>
<span data-selecao-lote-target="contador" class="text-sm text-gray-500">
Marque as que quer arquivar
</span>
<%# Começa HABILITADO. Se o JS não carregar (já aconteceu: os assets
ficaram 404 em produção), um botão que só o JS habilita deixaria a
função inalcançável — a marcação manual continua funcionando, e o
controller devolve um aviso quando nada foi marcado. %>
<button type="submit" data-selecao-lote-target="botao"
class="ml-auto min-h-[48px] px-4 py-2.5 rounded-lg whitespace-nowrap
text-sm font-semibold border border-[#2a2a2a] text-gray-300
hover:border-orange-500 hover:text-white transition-colors
disabled:opacity-40 disabled:cursor-not-allowed disabled:hover:border-[#2a2a2a]">
<%= icone :arquivadas %> Arquivar selecionadas
</button>
<% end %>
<% end %>
</div>
<%= render 'shared/carrossel_controles',
anterior: 'Consolidações anteriores', proximo: 'Próximas consolidações' %>
<%# Em celular/monitor pequeno a lista vira carrossel de 10 em 10; em tela
grande aparece inteira (ver carrossel_controller.js). %>
<div data-controller="carrossel" data-carrossel-tamanho-value="10" class="relative sm:px-16">
<div class="space-y-3">
<% @consolidacoes.each do |c| %>
<%# O card deixou de ser UM <a> só: com o link envolvendo tudo, clicar no
checkbox navegava para a consolidação em vez de marcar. Agora a
caixinha é irmã do link, e o link cobre o resto do card. %>
<div data-carrossel-target="item"
class="flex items-stretch bg-[#1a1a1a] border border-[#2a2a2a] rounded-xl
hover:border-orange-500 transition-colors overflow-hidden">
<% if pode_arquivar %>
<%# Alvo de toque grande (diretriz 3): a área clicável é a coluna
inteira, não os 20px do quadradinho. `form=` porque o checkbox
mora FORA do <form> — assim o formulário não precisa envolver a
lista (e o carrossel) inteira. %>
<label class="flex items-center pl-5 pr-3 cursor-pointer shrink-0 hover:bg-white/5"
aria-label="Selecionar <%= c.nome %>">
<input type="checkbox" name="ids[]" value="<%= c.id %>"
form="form-arquivar-lote"
data-selecao-lote-target="item"
data-action="change->selecao-lote#atualizar"
class="w-5 h-5 accent-orange-500">
</label>
<% end %>
<%= link_to consolidacao_path(c),
class: "flex-1 min-w-0 block p-5 #{'pl-0' if pode_arquivar}" do %>
<div class="flex flex-col md:flex-row md:items-center md:justify-between gap-3">
<div>
<div class="flex items-center gap-3 mb-1">
<h3 class="text-white font-bold text-lg"><%= c.nome %></h3>
<%= badge_status(c.status) %>
<% if c.finalizada? %><%= badge_pagamento(c.status_pagamento) %><% end %>
</div>
<p class="text-gray-400 text-sm">
<%= icone :calendario %> <%= l c.data_inicio, format: :short %> → <%= l c.data_fim, format: :short %>
· <%= icone :pessoa %> <%= c.consolidacao_motoristas.count %> motorista(s)
· Criada por <%= c.criador.nome_display %>
</p>
<% if c.operacoes.present? %>
<p class="text-gray-500 text-xs mt-1"><%= icone :pasta %> <%= c.operacoes_label.join(' · ') %></p>
<% end %>
</div>
<div class="text-right">
<p class="text-orange-500 font-black text-2xl"><%= moeda(c.valor_total) %></p>
<p class="text-gray-500 text-xs"><%= c.consolidacao_entregas.count %> entregas classificadas</p>
</div>
</div>
<% end %>
</div>
<% end %>
</div>
<%= render 'shared/carrossel_controles',
anterior: 'Consolidações anteriores', proximo: 'Próximas consolidações' %>
</div>
</div>
<% else %>
<div class="bg-[#1a1a1a] border border-[#2a2a2a] rounded-xl p-12 text-center">