107 lines
4.5 KiB
Ruby
107 lines
4.5 KiB
Ruby
# app/controllers/operacoes_dashboard_controller.rb
|
|
#
|
|
# Dashboard de análise de ENTREGAS por operação (separado do dashboard
|
|
# financeiro). O operador escolhe uma operação (UBS Norte, EMAD, ...) e vê os
|
|
# KPIs/insucessos/STS/mapa; também há os modos "global" (todas no período) e
|
|
# "comparar" (uma operação vs outra). Os números vêm de Analytics::OperacaoMetricas.
|
|
class OperacoesDashboardController < ApplicationController
|
|
MODOS = %w[operacao global comparar].freeze
|
|
|
|
# Linhas por página da tabela espelho (planilha da operação)
|
|
ESPELHO_POR_PAGINA = 15
|
|
|
|
def index
|
|
authorize :dashboard, :operacoes?
|
|
|
|
@periodo_inicio, @periodo_fim = periodo_selecionado
|
|
@modo = MODOS.include?(params[:modo]) ? params[:modo] : 'operacao'
|
|
@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, 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]
|
|
@op_b = Operacao.sanitizar([params[:op_b]]).first || @tabelas_validas[1]
|
|
@metricas_a = montar([@op_a]) if @op_a
|
|
@metricas_b = montar([@op_b]) if @op_b
|
|
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, data: @data_filtro) if @operacao
|
|
end
|
|
|
|
montar_tabela_espelho if @metricas && %w[operacao global].include?(@modo)
|
|
end
|
|
|
|
private
|
|
|
|
# Tabela espelho da planilha da operação: busca textual (q) + paginação (pg)
|
|
# sobre as linhas já cross-filtradas. Ordena pelas mais recentes (checkout;
|
|
# sem checkout vai para o fim).
|
|
def montar_tabela_espelho
|
|
@busca = params[:q].to_s.strip
|
|
linhas = @metricas.buscar(@busca).sort_by { |r| r['checkout'].to_s }.reverse
|
|
|
|
@espelho_total = linhas.size
|
|
@espelho_paginas = [(@espelho_total / ESPELHO_POR_PAGINA.to_f).ceil, 1].max
|
|
@espelho_pagina = params[:pg].to_i.clamp(1, @espelho_paginas)
|
|
@espelho_linhas = linhas[(@espelho_pagina - 1) * ESPELHO_POR_PAGINA, ESPELHO_POR_PAGINA] || []
|
|
end
|
|
|
|
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_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 = []
|
|
CROSS.each do |param, cfg|
|
|
valor = params[param].to_s.strip
|
|
next if valor.empty?
|
|
filtros[cfg[:coluna]] = valor
|
|
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
|
|
|
|
# Faixa de datas do filtro (params inicio/fim). Default: início do mês → hoje.
|
|
# Espelha DashboardController#periodo_selecionado.
|
|
def periodo_selecionado
|
|
inicio = parse_data(params[:inicio]) || Date.current.beginning_of_month
|
|
fim = parse_data(params[:fim]) || Date.current
|
|
inicio, fim = fim, inicio if fim < inicio
|
|
[inicio, fim]
|
|
end
|
|
|
|
def parse_data(str)
|
|
return nil if str.blank?
|
|
Date.parse(str)
|
|
rescue ArgumentError, TypeError
|
|
nil
|
|
end
|
|
end
|