Files
Reem-Notas/app/controllers/dashboard_controller.rb
victor bb1dead9fd Corrige 3 erros de runtime + filtro de faixa de datas no dashboard
Erros corrigidos (reportados em prints):
- Logout dava "No route matches [GET] /auth/logout": link usava method:
  :delete (rails-ujs), que o Turbo ignora -> agora data-turbo-method
- /admin/usuarios estourava NoMethodError 'admin_ou_gerente?'/'admin?' na
  UserPolicy -> adicionados helpers que delegam para o User
- Nova consolidação estourava NoMethodError 'new?' na ConsolidacaoPolicy ->
  ApplicationPolicy passa a definir new? (= create?) e edit? (= update?)

Dashboard:
- Substitui a navegação por mês por um filtro de calendário com faixa de
  datas (Flatpickr, range mode) que auto-submete inicio/fim
- Controller passa a consultar por período (Entrega.no_periodo) em vez de
  do_mes; gráfico diário limitado a ~1 trimestre

Specs: UserPolicy + new? na ConsolidacaoPolicy.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 22:27:57 -03:00

101 lines
3.5 KiB
Ruby

# app/controllers/dashboard_controller.rb
class DashboardController < ApplicationController
MAX_DIAS_GRAFICO = 92 # limita a granularidade diária do gráfico (≈ 1 trimestre)
def index
skip_authorization
# Motorista tem painel próprio — não enxerga o dashboard administrativo
return redirect_to(motorista_dashboard_path) if current_user.motorista?
@periodo_inicio, @periodo_fim = periodo_selecionado
carregar_dados_dashboard
end
private
# Faixa de datas vinda do filtro de calendário (params inicio/fim).
# Default: do início do mês corrente até hoje. Datas inválidas caem no default
# e a ordem é normalizada caso venham invertidas.
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
# Parse tolerante: data inválida/ausente vira nil (para o default assumir).
def parse_data(str)
return nil if str.blank?
Date.parse(str)
rescue ArgumentError, TypeError
nil
end
def carregar_dados_dashboard
# Entregas do período (tabela read-only) — restritas à conta Gade
entregas = Entrega.da_conta_gade.no_periodo(@periodo_inicio, @periodo_fim)
# Totais gerais
@total_entregas = entregas.count
@entregas_pagas = entregas.pagas.count
@entregas_pendentes = @total_entregas - @entregas_pagas
# Configurações de preço
config = Configuracao.mapa_de_precos
# Valor estimado total
@valor_estimado = (@entregas_pagas * config[:entrega]).round(2)
# Por motorista (top 10)
@motoristas = entregas.pagas
.group(:driver)
.count
.sort_by { |_, v| -v }
.first(10)
.map do |driver, qtd|
{
nome: driver,
entregas: qtd,
valor: (qtd * config[:entrega]).round(2)
}
end
# Por operação (route_id → contact_name)
@por_operacao = entregas.pagas
.group(:contact_name)
.count
.sort_by { |_, v| -v }
.first(6)
.to_h
# Evolução diária no período (para Chart.js)
@grafico_diario = build_grafico_diario(entregas, config[:entrega])
# Consolidações criadas no período
@consolidacoes_mes = Consolidacao.ativas.where(created_at: @periodo_inicio.beginning_of_day..@periodo_fim.end_of_day)
@consolidacoes_abertas = @consolidacoes_mes.where(status: :rascunho).count
@consolidacoes_fechadas = @consolidacoes_mes.where(status: :finalizada).count
# Histórico estimado mais recente
@historico_recente = HistoricoEstimado.order(created_at: :desc).limit(5)
end
def build_grafico_diario(entregas, preco_entrega)
# Em períodos muito longos, agrupa o gráfico de forma a não gerar centenas de pontos.
dias = (@periodo_inicio..@periodo_fim).to_a
return { labels: [], valores: [], qtds: [] } if dias.size > MAX_DIAS_GRAFICO
contagem = entregas.pagas
.group("DATE(planned_date)")
.count
.transform_keys(&:to_s)
labels = dias.map { |d| d.strftime('%d/%m') }
valores = dias.map { |d| ((contagem[d.to_s] || 0) * preco_entrega).round(2) }
qtds = dias.map { |d| contagem[d.to_s] || 0 }
{ labels: labels, valores: valores, qtds: qtds }
end
end