From 8d0d262bf8b28e8972bd863215bc630e49e92eeed319a45cb1f09d0c84f74b3a Mon Sep 17 00:00:00 2001 From: victor Date: Tue, 30 Jun 2026 18:05:20 -0300 Subject: [PATCH] Ajustes de UX --- .../operacoes_dashboard_controller.rb | 28 ++++++++++------ app/services/analytics/operacao_metricas.rb | 13 ++++---- app/views/layouts/_navbar.html.erb | 11 ++++--- app/views/operacoes_dashboard/index.html.erb | 32 +++++++++++++++++-- 4 files changed, 63 insertions(+), 21 deletions(-) diff --git a/app/controllers/operacoes_dashboard_controller.rb b/app/controllers/operacoes_dashboard_controller.rb index f565f46..930cb5c 100644 --- a/app/controllers/operacoes_dashboard_controller.rb +++ b/app/controllers/operacoes_dashboard_controller.rb @@ -15,11 +15,12 @@ class OperacoesDashboardController < ApplicationController @operacoes_agrupadas = Operacao.agrupadas_por_mes @tabelas_validas = Operacao.nomes_validos @filtros, @chips = cross_filtros + @data_filtro = parse_data(params[:f_data]) # clique no gráfico "Entregas por Dia" case @modo when 'global' # Global: agrega TODAS as operações dentro da faixa de datas escolhida. - @metricas = montar(@tabelas_validas, inicio: @periodo_inicio, fim: @periodo_fim, filtros: @filtros) + @metricas = montar(@tabelas_validas, inicio: @periodo_inicio, fim: @periodo_fim, filtros: @filtros, data: @data_filtro) when 'comparar' # Comparar: cada operação inteira (sem filtro de data — só vale no Global). @op_a = Operacao.sanitizar([params[:op_a]]).first || @tabelas_validas[0] @@ -29,25 +30,29 @@ class OperacoesDashboardController < ApplicationController else # Operação única: a operação inteira (a data vem dos próprios dados). @operacao = Operacao.sanitizar([params[:operacao]]).first || @tabelas_validas.first - @metricas = montar([@operacao], filtros: @filtros) if @operacao + @metricas = montar([@operacao], filtros: @filtros, data: @data_filtro) if @operacao end end private - def montar(tabelas, inicio: nil, fim: nil, filtros: {}) - Analytics::OperacaoMetricas.new(tabelas: tabelas, inicio: inicio, fim: fim, filtros: filtros) + def montar(tabelas, inicio: nil, fim: nil, filtros: {}, data: nil) + Analytics::OperacaoMetricas.new(tabelas: tabelas, inicio: inicio, fim: fim, filtros: filtros, data: data) end # Cross-filter por clique nas tabelas. Cada param vira um filtro de coluna e um # "chip" removível na tela. Retorna [filtros (coluna=>valor), chips (p/ a view)]. CROSS = { - 'f_driver' => { coluna: 'driver', rotulo: 'Motorista' }, - 'f_sts' => { coluna: 'contact_name', rotulo: 'Unidade' }, - 'f_status' => { coluna: 'status_gade', rotulo: 'Status' }, - 'f_obs' => { coluna: 'observation', rotulo: 'Observação' } + 'f_driver' => { coluna: 'driver', rotulo: 'Motorista' }, + 'f_sts' => { coluna: 'contact_name', rotulo: 'Unidade' }, + 'f_status' => { coluna: 'status_gade', rotulo: 'Status' }, + 'f_obs' => { coluna: 'observation', rotulo: 'Observação' }, + 'f_resultado' => { coluna: 'status', rotulo: 'Resultado' } }.freeze + # Rótulos amigáveis para o filtro de Resultado (clique no donut "Insucessos %"). + RESULTADO_LABEL = { 'completed' => 'Completas', 'failed' => 'Falhas' }.freeze + def cross_filtros filtros = {} chips = [] @@ -55,7 +60,12 @@ class OperacoesDashboardController < ApplicationController valor = params[param].to_s.strip next if valor.empty? filtros[cfg[:coluna]] = valor - chips << { param: param, valor: valor, rotulo: cfg[:rotulo] } + display = param == 'f_resultado' ? (RESULTADO_LABEL[valor] || valor) : valor + chips << { param: param, valor: valor, display: display, rotulo: cfg[:rotulo] } + end + # Filtro por DIA (clique no gráfico "Entregas por Dia") — tratado à parte (data). + if (dia = parse_data(params[:f_data])) + chips << { param: 'f_data', valor: params[:f_data].to_s, display: dia.strftime('%d/%m/%Y'), rotulo: 'Dia' } end [filtros, chips] end diff --git a/app/services/analytics/operacao_metricas.rb b/app/services/analytics/operacao_metricas.rb index 97080aa..97c2834 100644 --- a/app/services/analytics/operacao_metricas.rb +++ b/app/services/analytics/operacao_metricas.rb @@ -24,11 +24,12 @@ module Analytics # filtros: Hash de cross-filter (coluna => valor) aplicado em memória — clicar # numa célula da tabela (motorista/STS/status/observação) refiltra TODO o # dashboard por aquele valor. Ex.: { 'driver' => 'Carlos' }. - def initialize(tabelas:, inicio: nil, fim: nil, filtros: {}) + def initialize(tabelas:, inicio: nil, fim: nil, filtros: {}, data: nil) @tabelas = Operacao.sanitizar(Array(tabelas)) @inicio = inicio&.to_date @fim = fim&.to_date @filtros = (filtros || {}).reject { |_, v| v.to_s.strip.empty? } + @data = data&.to_date # filtro por DIA (clique no gráfico "Entregas por Dia") end def operacoes_label @@ -37,11 +38,11 @@ module Analytics # Linhas após o cross-filter — base de TODAS as agregações/KPIs. def linhas - @linhas ||= if @filtros.empty? - registros - else - registros.select { |r| @filtros.all? { |col, val| r[col].to_s == val.to_s } } - end + @linhas ||= begin + base = @filtros.empty? ? registros + : registros.select { |r| @filtros.all? { |col, val| r[col].to_s == val.to_s } } + @data ? base.select { |r| data_de(r['checkout']) == @data } : base + end end # Taxas (úteis no comparativo). diff --git a/app/views/layouts/_navbar.html.erb b/app/views/layouts/_navbar.html.erb index 6d6994c..f6c2027 100644 --- a/app/views/layouts/_navbar.html.erb +++ b/app/views/layouts/_navbar.html.erb @@ -72,12 +72,15 @@ <%# Botão flutuante (desktop) — esconde/mostra a sidebar do site inteiro. - Fica invisível e só aparece quando o mouse chega perto (zona de hover). %> + Em repouso mostra uma ALÇA fina laranja (indicador de que dá p/ recolher); + ao chegar perto com o mouse, ela expande no botão com a seta. %>