Fases 4, 5 e 6 — Job 1h + Auditoria + Consolidação + Wizard de validação

This commit is contained in:
2026-06-11 12:02:50 -03:00
parent b77fc0122c
commit 90ed705ed8
29 changed files with 1617 additions and 616 deletions

View File

@@ -1,82 +1,46 @@
# app/helpers/application_helper.rb
module ApplicationHelper
# Formata valor monetário → "R$ 1.250,00"
# 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
def moeda(valor)
"R$ #{format('%.2f', valor.to_f).gsub('.', ',')}"
"R$ #{"%.2f" % valor.to_f}".gsub('.', ',')
end
# Link ativo na sidebar
def nav_link_to(nome, path, icon: nil, **opts)
ativo = current_page?(path) || request.path.start_with?(path.to_s)
classes = [
'flex items-center gap-3 px-4 py-3 rounded-xl text-sm font-medium transition-all',
ativo ? 'bg-[#f97316] text-white shadow-lg shadow-orange-500/20'
: 'text-gray-400 hover:text-white hover:bg-white/5'
].join(' ')
link_to path, class: classes, **opts do
concat content_tag(:span, icon, class: 'text-lg w-5 text-center') if icon
concat content_tag(:span, nome)
end
end
# Badge colorido por status de consolidação
# Badge de status de consolidação
def badge_status(status)
config = {
'aberta' => { bg: 'bg-yellow-500/20', text: 'text-yellow-400', label: 'Aberta' },
'em_revisao' => { bg: 'bg-blue-500/20', text: 'text-blue-400', label: 'Em Revisão' },
'fechada' => { bg: 'bg-green-500/20', text: 'text-green-400', label: 'Fechada' },
'aprovada' => { bg: 'bg-[#f97316]/20', text: 'text-[#f97316]', label: 'Aprovada' },
'cancelada' => { bg: 'bg-red-500/20', text: 'text-red-400', label: 'Cancelada' }
}
c = config[status.to_s] || { bg: 'bg-gray-500/20', text: 'text-gray-400', label: status.to_s.humanize }
content_tag(:span, c[:label],
class: "inline-flex items-center px-2.5 py-1 rounded-lg text-xs font-medium #{c[:bg]} #{c[:text]}")
end
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
# Badge de role do usuário
def badge_role(role)
config = {
'admin' => { bg: 'bg-purple-500/20', text: 'text-purple-400', label: 'Admin' },
'gerente' => { bg: 'bg-blue-500/20', text: 'text-blue-400', label: 'Gerente' },
'operador' => { bg: 'bg-cyan-500/20', text: 'text-cyan-400', label: 'Operador' },
'motorista'=> { bg: 'bg-green-500/20', text: 'text-green-400', label: 'Motorista' }
}
c = config[role.to_s] || { bg: 'bg-gray-500/20', text: 'text-gray-400', label: role.to_s.humanize }
content_tag(:span, c[:label],
class: "inline-flex items-center px-2.5 py-1 rounded-lg text-xs font-medium #{c[:bg]} #{c[:text]}")
end
# Badge de status ativo/inativo do usuário
def badge_status_usuario(ativo)
if ativo
content_tag(:span, 'Ativo',
class: 'inline-flex items-center px-2.5 py-1 rounded-lg text-xs font-medium bg-green-500/20 text-green-400')
else
content_tag(:span, 'Inativo',
class: 'inline-flex items-center px-2.5 py-1 rounded-lg text-xs font-medium bg-red-500/20 text-red-400')
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
# Barra de progresso
def progress_bar(percentual, color: '#f97316')
perc = [[percentual.to_i, 0].max, 100].min
content_tag(:div, class: 'w-full bg-white/10 rounded-full h-2') do
content_tag(:div, '',
class: 'h-2 rounded-full transition-all duration-500',
style: "width: #{perc}%; background-color: #{color}")
# 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
# Opções de role para select
def role_options_for_select
User.roles.keys.map { |r| [r.humanize, r] }
end
# Classe base para inputs
def input_class
'w-full px-4 py-3 bg-[#0a0a0a] border border-white/10 rounded-xl text-white ' \
'placeholder-gray-600 focus:outline-none focus:border-[#f97316] focus:ring-1 ' \
'focus:ring-[#f97316] transition-colors'
end
end