Files
Reem-Notas/app/helpers/application_helper.rb

128 lines
5.3 KiB
Ruby

# app/helpers/application_helper.rb
module ApplicationHelper
include Pagy::Frontend
# Opções [label, valor] para o select de perfil de usuário (form de usuário)
def role_options_for_select
User::ROLES_LABEL.map { |value, label| [label, value] }
end
# Link de navegação com estado ativo
def nav_link_to(label, path, **opts)
active = current_page?(path)
css = [
"flex items-center gap-3 px-3 py-2.5 rounded-lg text-sm font-medium transition-all min-h-[44px]",
active ? "bg-orange-500 text-black" : "text-gray-400 hover:bg-[#1a1a1a] hover:text-white"
].join(" ")
link_to label, path, class: css
end
# Formata moeda BR (milhar com ".", decimais com ","): R$ 1.234,56
def moeda(valor)
number_to_currency(valor.to_f, unit: 'R$ ', separator: ',', delimiter: '.', precision: 2)
end
# Badge de status de consolidação
def badge_status(status)
cfg = case status.to_s
when 'rascunho' then { cor: 'bg-yellow-500 text-black', icone: '📝', label: 'Rascunho' }
when 'finalizada' then { cor: 'bg-green-600 text-white', icone: '✅', label: 'Finalizada' }
when 'arquivada' then { cor: 'bg-gray-700 text-gray-300', icone: '📁', label: 'Arquivada' }
else { cor: 'bg-gray-800 text-gray-400', icone: '❓', label: status.humanize }
end
tag.span class: "inline-flex items-center gap-1 px-2.5 py-1 rounded-full text-xs font-bold #{cfg[:cor]}" do
"#{cfg[:icone]} #{cfg[:label]}"
end
end
# Badge de status de pagamento de uma consolidação (derivado dos motoristas)
def badge_pagamento(status)
cfg = case status.to_s
when 'pago' then { cor: 'bg-green-600 text-white', icone: '💰', label: 'Pago' }
when 'parcial' then { cor: 'bg-yellow-500 text-black', icone: '⏳', label: 'Parcial' }
when 'pendente' then { cor: 'bg-gray-700 text-gray-300', icone: '🕗', label: 'Pendente' }
else { cor: 'bg-gray-800 text-gray-400', icone: '❓', label: status.to_s.humanize }
end
tag.span class: "inline-flex items-center gap-1 px-2.5 py-1 rounded-full text-xs font-bold #{cfg[:cor]}" do
"#{cfg[:icone]} #{cfg[:label]}"
end
end
# Badge de papel do usuário (admin/gerente/operador/motorista)
def badge_role(role)
cfg = case role.to_s
when 'admin' then { cor: 'bg-orange-500 text-black' }
when 'gerente' then { cor: 'bg-orange-800 text-white' }
when 'operador' then { cor: 'bg-gray-700 text-white' }
when 'motorista' then { cor: 'bg-gray-900 text-orange-400 border border-orange-500' }
when 'externo' then { cor: 'bg-blue-900 text-blue-200 border border-blue-500' }
else { cor: 'bg-gray-800 text-gray-400' }
end
label = User::ROLES_LABEL[role.to_s] || role.to_s.humanize
tag.span label, class: "inline-flex items-center px-2.5 py-1 rounded-full text-xs font-bold #{cfg[:cor]}"
end
# Badge de status ativo/inativo do usuário
def badge_status_usuario(ativo)
cor = ativo ? 'bg-green-600 text-white' : 'bg-red-600 text-white'
label = ativo ? 'Ativo' : 'Inativo'
tag.span label, class: "inline-flex items-center px-2.5 py-1 rounded-full text-xs font-bold #{cor}"
end
# Horário da última sincronização dos dados de operação.
#
# O backend atualiza os dados a cada 30 min, das 08h às 18h (horário de
# Brasília). Como as tabelas externas de rastreio não têm um carimbo de
# sincronização confiável, derivamos o último "slot" concluído a partir dessa
# grade fixa. Usa Time.current (fuso do app), NUNCA Time.now — que pega o fuso
# do container (UTC) e por isso mostrava o horário adiantado/errado.
DADOS_SYNC_INICIO = 8 # abre às 08h
DADOS_SYNC_FIM = 18 # fecha às 18h
DADOS_SYNC_PASSO = 30 # minutos entre atualizações
def ultima_atualizacao_dados(agora = Time.current)
agora = agora.in_time_zone
abre = agora.change(hour: DADOS_SYNC_INICIO, min: 0)
fecha = agora.change(hour: DADOS_SYNC_FIM, min: 0)
if agora < abre
# Antes de abrir: a última atualização foi o fechamento do dia anterior.
(abre - 1.day).change(hour: DADOS_SYNC_FIM, min: 0)
elsif agora >= fecha
fecha
else
# Arredonda para baixo ao múltiplo de 30 min dentro da janela.
minuto = (agora.min / DADOS_SYNC_PASSO) * DADOS_SYNC_PASSO
agora.change(min: minuto, sec: 0)
end
end
# Rótulo amigável: "às 14:30" (hoje) ou "em 05/07 às 18:00" (outro dia).
def ultima_atualizacao_label(agora = Time.current)
t = ultima_atualizacao_dados(agora)
if t.to_date == agora.in_time_zone.to_date
"às #{t.strftime('%H:%M')}"
else
"em #{t.strftime('%d/%m')} às #{t.strftime('%H:%M')}"
end
end
# Barra de progresso laranja
def progress_bar(percentual, label: nil)
pct = percentual.to_i.clamp(0, 100)
tag.div class: "w-full" do
concat tag.div(class: "flex justify-between text-xs text-gray-400 mb-1") {
concat tag.span(label || "#{pct}% classificado")
concat tag.span("#{pct}%")
}
concat tag.div(class: "w-full bg-[#2a2a2a] rounded-full h-2") {
tag.div style: "width: #{pct}%",
class: "h-2 rounded-full bg-orange-500 transition-all duration-500"
}
end
end
end