implantação da fase 2 e 3

This commit is contained in:
2026-06-10 20:12:20 -03:00
parent d37237ead2
commit 472484798f
33 changed files with 2915 additions and 430 deletions

View File

@@ -4,24 +4,26 @@ class ApplicationController < ActionController::Base
before_action :authenticate_user!
before_action :set_tema
after_action :verify_authorized, except: :index, unless: :skip_pundit?
after_action :verify_policy_scoped, only: :index, unless: :skip_pundit?
# Pundit: redireciona se não autorizado
rescue_from Pundit::NotAuthorizedError do |e|
flash[:alert] = "Você não tem permissão para realizar esta ação."
redirect_to root_path
end
rescue_from Pundit::NotAuthorizedError, with: :usuario_nao_autorizado
protect_from_forgery with: :exception
private
def set_tema
@tema = current_user&.tema_preferido || 'dark'
# Lê o tema da sessão (padrão: escuro)
@tema = session[:tema] || 'dark'
end
def after_sign_in_path_for(resource)
if resource.motorista?
motorista_dashboard_path
else
dashboard_path
end
def skip_pundit?
devise_controller? || params[:controller] =~ /rails\/|action_mailbox|action_text/
end
def usuario_nao_autorizado
flash[:alert] = 'Acesso negado. Você não tem permissão para esta ação.'
redirect_back(fallback_location: dashboard_path)
end
end

View File

@@ -0,0 +1,44 @@
# app/controllers/configuracoes_controller.rb
class ConfiguracoesController < ApplicationController
before_action :set_configuracao, only: [:edit, :update]
def index
authorize Configuracao
@configuracoes = Configuracao.all.order(:tipo)
end
def edit
authorize @configuracao
end
def update
authorize @configuracao
valor_anterior = @configuracao.valor
if @configuracao.update(configuracao_params)
AuditoriaLog.registrar(current_user, 'editar_preco', request.remote_ip,
detalhes: "#{@configuracao.tipo}: R$ #{valor_anterior} → R$ #{@configuracao.valor}")
redirect_to configuracoes_path, notice: "Preço de #{@configuracao.tipo_label} atualizado."
else
render :edit, status: :unprocessable_entity
end
end
# POST /configuracoes/toggle_tema
def toggle_tema
skip_authorization
session[:tema] = session[:tema] == 'light' ? 'dark' : 'light'
redirect_back(fallback_location: dashboard_path)
end
private
def set_configuracao
@configuracao = Configuracao.find(params[:id])
end
def configuracao_params
params.require(:configuracao).permit(:valor, :descricao)
end
end

View File

@@ -0,0 +1,77 @@
# app/controllers/dashboard_controller.rb
class DashboardController < ApplicationController
def index
skip_authorization
@data_referencia = params[:data] ? Date.parse(params[:data]) : Date.today
@mes_inicio = @data_referencia.beginning_of_month
@mes_fim = @data_referencia.end_of_month
carregar_dados_dashboard
end
private
def carregar_dados_dashboard
# Entregas do mês atual (da tabela read-only)
entregas_mes = Entrega.do_mes(@data_referencia)
# Totais gerais
@total_entregas = entregas_mes.count
@entregas_pagas = entregas_mes.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_mes.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_mes.pagas
.group(:contact_name)
.count
.sort_by { |_, v| -v }
.first(6)
# Evolução diária do mês (para Chart.js)
@grafico_diario = build_grafico_diario(entregas_mes, config[:entrega])
# Consolidações do mês
@consolidacoes_mes = Consolidacao.do_mes(@data_referencia)
@consolidacoes_abertas = @consolidacoes_mes.where(status: :aberta).count
@consolidacoes_fechadas = @consolidacoes_mes.where(status: :fechada).count
# Histórico estimado mais recente
@historico_recente = HistoricoEstimado.order(created_at: :desc).limit(5)
end
def build_grafico_diario(entregas, preco_entrega)
dias = ((@mes_inicio)..[@mes_fim, Date.today].min).map(&:to_s)
contagem = entregas.pagas
.group("DATE(planned_date)")
.count
.transform_keys { |k| k.to_s }
labels = dias.map { |d| Date.parse(d).strftime('%d/%m') }
valores = dias.map { |d| ((contagem[d] || 0) * preco_entrega).round(2) }
qtds = dias.map { |d| contagem[d] || 0 }
{ labels: labels, valores: valores, qtds: qtds }
end
end

View File

@@ -0,0 +1,42 @@
# app/controllers/motorista_controller.rb
class MotoristaController < ApplicationController
before_action :verificar_motorista
def index
skip_authorization
@data_referencia = params[:data] ? Date.parse(params[:data]) : Date.today
config = Configuracao.mapa_de_precos
# Entregas do motorista logado
@entregas_mes = Entrega
.do_mes(@data_referencia)
.where("LOWER(driver) = ?", current_user.name.downcase)
@total_entregas = @entregas_mes.count
@pagas = @entregas_mes.pagas.count
@pendentes = @total_entregas - @pagas
@valor_estimado = (@pagas * config[:entrega]).round(2)
# Últimas entregas
@ultimas_entregas = @entregas_mes.pagas
.order(planned_date: :desc)
.limit(10)
# Consolidações do motorista
@consolidacoes = ConsolidacaoMotorista
.joins(:consolidacao)
.where(user: current_user)
.where(consolidacoes: { status: [:fechada, :aprovada] })
.order('consolidacoes.created_at DESC')
.limit(5)
end
private
def verificar_motorista
unless current_user&.motorista?
redirect_to dashboard_path, alert: 'Acesso restrito a motoristas.'
end
end
end

View File

@@ -0,0 +1,56 @@
# app/controllers/users/sessions_controller.rb
class Users::SessionsController < Devise::SessionsController
before_action :configure_sign_in_params, only: [:create]
# GET /users/sign_in
def new
@pin_login = params[:pin].present?
super
end
# POST /users/sign_in
def create
# PIN login para motoristas
if params[:pin_login].present?
handle_pin_login
else
super
end
end
protected
def after_sign_in_path_for(resource)
case resource.role
when 'motorista'
motorista_path
else
dashboard_path
end
end
def after_sign_out_path_for(resource_or_scope)
new_user_session_path
end
def configure_sign_in_params
devise_parameter_sanitizer.permit(:sign_in, keys: [:email, :password, :pin])
end
private
def handle_pin_login
pin = params[:user][:pin].to_s.strip
user = User.find_by(pin_acesso: pin, role: 'motorista')
if user&.active?
sign_in(user)
AuditoriaLog.registrar(user, 'login_pin', request.remote_ip)
redirect_to motorista_path, notice: "Bem-vindo, #{user.nome_display}!"
else
flash.now[:alert] = 'PIN inválido ou motorista inativo.'
@pin_login = true
render :new, status: :unprocessable_entity
end
end
end

View File

@@ -0,0 +1,88 @@
# app/controllers/usuarios_controller.rb
class UsuariosController < ApplicationController
before_action :set_usuario, only: [:show, :edit, :update, :destroy, :toggle_ativo]
def index
authorize User
@usuarios = policy_scope(User).order(:name).page(params[:page]).per(20)
@roles = User.roles.keys
end
def show
authorize @usuario
end
def new
@usuario = User.new
authorize @usuario
end
def create
@usuario = User.new(usuario_params)
authorize @usuario
if @usuario.save
AuditoriaLog.registrar(current_user, 'criar_usuario', request.remote_ip,
detalhes: "Criou usuário #{@usuario.email} (#{@usuario.role})")
redirect_to usuarios_path, notice: 'Usuário criado com sucesso.'
else
render :new, status: :unprocessable_entity
end
end
def edit
authorize @usuario
end
def update
authorize @usuario
if @usuario.update(usuario_params_update)
AuditoriaLog.registrar(current_user, 'editar_usuario', request.remote_ip,
detalhes: "Editou usuário #{@usuario.email}")
redirect_to usuarios_path, notice: 'Usuário atualizado.'
else
render :edit, status: :unprocessable_entity
end
end
def destroy
authorize @usuario
if @usuario == current_user
redirect_to usuarios_path, alert: 'Você não pode excluir sua própria conta.'
return
end
@usuario.destroy
AuditoriaLog.registrar(current_user, 'excluir_usuario', request.remote_ip,
detalhes: "Excluiu usuário #{@usuario.email}")
redirect_to usuarios_path, notice: 'Usuário removido.'
end
def toggle_ativo
authorize @usuario, :update?
@usuario.update!(ativo: !@usuario.ativo)
status = @usuario.ativo? ? 'ativado' : 'desativado'
AuditoriaLog.registrar(current_user, "usuario_#{status}", request.remote_ip,
detalhes: "#{@usuario.email} foi #{status}")
redirect_to usuarios_path, notice: "Usuário #{status} com sucesso."
end
private
def set_usuario
@usuario = User.find(params[:id])
end
def usuario_params
params.require(:user).permit(:name, :email, :password, :password_confirmation,
:role, :pin_acesso, :ativo)
end
def usuario_params_update
permitted = [:name, :email, :role, :pin_acesso, :ativo]
permitted += [:password, :password_confirmation] if params[:user][:password].present?
params.require(:user).permit(permitted)
end
end