teste #8

Merged
victor merged 4 commits from teste into main 2026-07-20 22:26:52 -03:00
34 changed files with 550 additions and 337 deletions
Showing only changes of commit 42d06c50a7 - Show all commits

View File

@@ -2,19 +2,105 @@
module ApplicationHelper module ApplicationHelper
include Pagy::Frontend include Pagy::Frontend
# ---------------------------------------------------------------------------
# Ícones
#
# Substituem os emojis antigos (que davam aparência "genérica de IA" e variavam
# de forma/cor conforme o sistema operacional). Agora são SVG chapados vindos do
# sprite em public/icons.svg (subset do Bootstrap Icons, licença MIT).
#
# O SVG usa `currentColor`, então a cor vem da classe CSS:
# - padrão → laranja da marca (#f97316)
# - ícones de status → passe `cor:` para preservar a leitura semântica
# (ex.: icone(:sucesso, cor: "text-green-400"))
# ---------------------------------------------------------------------------
# Nome semântico (pt-BR) → id do símbolo no sprite.
ICONES = {
# Navegação
dashboard: "bar-chart-fill", operacoes: "graph-up-arrow", consolidacoes: "box-seam-fill",
arquivadas: "folder-fill", painel: "house-fill", usuarios: "people-fill",
configuracoes: "gear-fill", auditoria: "search", planilha: "box-arrow-in-down",
editar_lancamento: "pencil-fill", sair: "box-arrow-right", menu: "list",
# Domínio
caminhao: "truck", veiculo: "car-front-fill", motorista: "person-badge-fill",
dinheiro: "cash-coin", pagamento: "cash-stack", custo: "cash",
consolidado_pago: "briefcase-fill", calendario: "calendar-event", nota_fiscal: "receipt",
documento: "file-earmark-text-fill", termo_especial: "clipboard-data-fill",
apontamento: "pencil-square", local: "geo-alt-fill", operacao: "hospital-fill",
retirada: "box-seam-fill", desconto: "exclamation-triangle-fill",
global: "globe2", comparar: "arrow-left-right", pessoa: "person-fill",
ticket: "ticket-perforated-fill", tag: "tag-fill", composicao: "puzzle-fill",
ranking: "trophy-fill", historico: "clock-history", relogio: "clock-fill",
parcial: "hourglass-split", diario: "journal-text", vazio: "inbox",
dica: "lightbulb-fill", bonus: "star-fill", extraordinaria: "stars",
calor: "fire", foto: "camera-fill", mapa: "map-fill",
satelite: "broadcast-pin", escuro: "moon-fill", tela_cheia: "arrows-fullscreen",
email: "envelope-fill", telefone: "telephone-fill", chave: "key-fill",
# Ações
adicionar: "plus-lg", fechar: "x-lg", confirmar: "check-lg",
buscar: "search", editar: "pencil-fill", excluir: "trash-fill",
arquivar: "archive-fill", restaurar: "arrow-clockwise", salvar: "floppy-fill",
imprimir: "printer-fill", visualizar: "eye-fill", baixar: "download",
pasta: "folder2-open", travado: "lock-fill", destravado: "unlock-fill",
expandir: "chevron-down", recolher: "chevron-left",
voltar: "arrow-left", avancar: "arrow-right", estornar: "arrow-counterclockwise",
# Status (use sempre com `cor:` semântica)
sucesso: "check-circle-fill", alerta: "exclamation-triangle-fill",
erro: "x-circle-fill", desconhecido: "question-circle",
cancelado: "slash-circle", ponto: "circle-fill", ponto_vazio: "circle"
}.freeze
# Renderiza um ícone do sprite.
#
# icone(:caminhao) # laranja da marca
# icone(:sucesso, cor: "text-green-400") # status verde
# icone(:fechar, cor: nil) # herda a cor do elemento pai
# icone(:dashboard, titulo: "Painel") # acessível (vira role="img")
def icone(nome, cor: "text-brand-laranja", tamanho: "w-[1.05em] h-[1.05em]", classe: nil, titulo: nil)
simbolo = ICONES.fetch(nome.to_sym) { nome.to_s }
css = ["inline-block align-[-0.125em] shrink-0", tamanho, cor, classe].compact.join(" ")
tag.svg(class: css, fill: "currentColor", role: (titulo ? "img" : nil),
aria: { hidden: (titulo ? nil : true), label: titulo }) do
tag.use(href: "/icons.svg##{simbolo}")
end
end
# Rótulo "ícone + texto" para link_to / button_to (que recebem o label como
# argumento, onde não dá para escrever ERB).
#
# link_to rotulo(:salvar, 'Salvar rascunho'), caminho, class: '...'
#
# Atenção: NÃO serve para `f.submit` nem `button_to` sem bloco — ambos geram
# <input type="submit">, que não aceita HTML dentro. Nesses casos use
# `button_tag`/`button_to ... do`.
def rotulo(nome, texto, cor: "text-brand-laranja", tamanho: "w-4 h-4")
safe_join([icone(nome, cor: cor, tamanho: tamanho), texto], " ")
end
# Opções [label, valor] para o select de perfil de usuário (form de usuário) # Opções [label, valor] para o select de perfil de usuário (form de usuário)
def role_options_for_select def role_options_for_select
User::ROLES_LABEL.map { |value, label| [label, value] } User::ROLES_LABEL.map { |value, label| [label, value] }
end end
# Link de navegação com estado ativo # Link de navegação com estado ativo.
def nav_link_to(label, path, **opts) # `icon:` aceita um nome semântico de ICONES; no estado ativo o ícone herda o
# preto do fundo laranja, fora dele fica laranja da marca.
def nav_link_to(label, path, icon: nil, **opts)
active = current_page?(path) active = current_page?(path)
css = [ css = [
"flex items-center gap-3 px-3 py-2.5 rounded-lg text-sm font-medium transition-all min-h-[44px]", "flex items-center gap-2.5 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" active ? "bg-orange-500 text-black" : "text-gray-400 hover:bg-[#1a1a1a] hover:text-white"
].join(" ") ].join(" ")
link_to label, path, class: css
conteudo = if icon
safe_join([icone(icon, cor: (active ? "text-black" : "text-brand-laranja"),
tamanho: "w-4 h-4"), label])
else
label
end
link_to conteudo, path, class: css
end end
# Formata moeda BR (milhar com ".", decimais com ","): R$ 1.234,56 # Formata moeda BR (milhar com ".", decimais com ","): R$ 1.234,56
@@ -22,31 +108,34 @@ module ApplicationHelper
number_to_currency(valor.to_f, unit: 'R$ ', separator: ',', delimiter: '.', precision: 2) number_to_currency(valor.to_f, unit: 'R$ ', separator: ',', delimiter: '.', precision: 2)
end end
# Badge de status de consolidação # Badge de status de consolidação.
# O ícone herda a cor do texto do badge (cor: nil) para não brigar com o fundo.
def badge_status(status) def badge_status(status)
cfg = case status.to_s cfg = case status.to_s
when 'rascunho' then { cor: 'bg-yellow-500 text-black', icone: '📝', label: 'Rascunho' } when 'rascunho' then { cor: 'bg-yellow-500 text-black', icone: :apontamento, label: 'Rascunho' }
when 'finalizada' then { cor: 'bg-green-600 text-white', icone: '✅', label: 'Finalizada' } when 'finalizada' then { cor: 'bg-green-600 text-white', icone: :sucesso, label: 'Finalizada' }
when 'arquivada' then { cor: 'bg-gray-700 text-gray-300', icone: '📁', label: 'Arquivada' } when 'arquivada' then { cor: 'bg-gray-700 text-gray-300', icone: :arquivar, label: 'Arquivada' }
else { cor: 'bg-gray-800 text-gray-400', icone: '❓', label: status.humanize } else { cor: 'bg-gray-800 text-gray-400', icone: :desconhecido, label: status.humanize }
end end
tag.span class: "inline-flex items-center gap-1 px-2.5 py-1 rounded-full text-xs font-bold #{cfg[:cor]}" do badge_com_icone(cfg)
"#{cfg[:icone]} #{cfg[:label]}"
end
end end
# Badge de status de pagamento de uma consolidação (derivado dos motoristas) # Badge de status de pagamento de uma consolidação (derivado dos motoristas)
def badge_pagamento(status) def badge_pagamento(status)
cfg = case status.to_s cfg = case status.to_s
when 'pago' then { cor: 'bg-green-600 text-white', icone: '💰', label: 'Pago' } when 'pago' then { cor: 'bg-green-600 text-white', icone: :dinheiro, label: 'Pago' }
when 'parcial' then { cor: 'bg-yellow-500 text-black', icone: '⏳', label: 'Parcial' } when 'parcial' then { cor: 'bg-yellow-500 text-black', icone: :parcial, label: 'Parcial' }
when 'pendente' then { cor: 'bg-gray-700 text-gray-300', icone: '🕗', label: 'Pendente' } when 'pendente' then { cor: 'bg-gray-700 text-gray-300', icone: :relogio, label: 'Pendente' }
else { cor: 'bg-gray-800 text-gray-400', icone: '❓', label: status.to_s.humanize } else { cor: 'bg-gray-800 text-gray-400', icone: :desconhecido, label: status.to_s.humanize }
end end
tag.span class: "inline-flex items-center gap-1 px-2.5 py-1 rounded-full text-xs font-bold #{cfg[:cor]}" do badge_com_icone(cfg)
"#{cfg[:icone]} #{cfg[:label]}" end
def badge_com_icone(cfg)
tag.span class: "inline-flex items-center gap-1.5 px-2.5 py-1 rounded-full text-xs font-bold #{cfg[:cor]}" do
safe_join([icone(cfg[:icone], cor: nil, tamanho: "w-3.5 h-3.5"), cfg[:label]])
end end
end end

View File

@@ -263,10 +263,12 @@ export default class extends Controller {
const lock = chip.querySelector("[data-chip-lock]") const lock = chip.querySelector("[data-chip-lock]")
if (feito) feito.textContent = dados.classificadas if (feito) feito.textContent = dados.classificadas
if (total) total.textContent = dados.eligible if (total) total.textContent = dados.eligible
if (check) check.textContent = dados.completo ? "✓" : "" // O check e o cadeado agora são <svg> fixos no HTML — alterna a visibilidade
// em vez de trocar textContent (que apagaria o SVG).
if (check) check.classList.toggle("hidden", !dados.completo)
if (lock) lock.classList.toggle("hidden", !dados.fechado) if (lock) lock.classList.toggle("hidden", !dados.fechado)
// Cor determinística: selecionado(laranja) > consolidado(verde+🔒) > // Cor determinística: selecionado(laranja) > consolidado(cadeado+verde) >
// pronto(verde) > arquivado(cinza) > neutro. // pronto(verde) > arquivado(cinza) > neutro.
chip.classList.remove(...TODAS) chip.classList.remove(...TODAS)
if (this.veiculosValue.includes(chip.dataset.vehicle)) { if (this.veiculosValue.includes(chip.dataset.vehicle)) {

View File

@@ -1,7 +1,7 @@
<%# app/views/admin/auditoria_logs/index.html.erb %> <%# app/views/admin/auditoria_logs/index.html.erb %>
<% content_for :title, 'Auditoria' %> <% content_for :title, 'Auditoria' %>
<h1 class="text-2xl font-bold text-white mb-6">🔍 Auditoria</h1> <h1 class="text-2xl font-bold text-white mb-6 flex items-center gap-2"><%= icone :auditoria %> Auditoria</h1>
<%= form_with url: admin_auditoria_logs_path, method: :get, <%= form_with url: admin_auditoria_logs_path, method: :get,
class: 'bg-[#1a1a1a] border border-[#2a2a2a] rounded-xl p-4 mb-6 flex flex-wrap gap-3' do %> class: 'bg-[#1a1a1a] border border-[#2a2a2a] rounded-xl p-4 mb-6 flex flex-wrap gap-3' do %>

View File

@@ -10,7 +10,7 @@
<%# Pilares de preço %> <%# Pilares de preço %>
<div> <div>
<h2 class="text-lg font-semibold text-white mb-4">💰 Pilares de Preço</h2> <h2 class="text-lg font-semibold text-white mb-4 flex items-center gap-2"><%= icone :dinheiro %> Pilares de Preço</h2>
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4"> <div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4">
<% @configuracoes.each do |cfg| %> <% @configuracoes.each do |cfg| %>
<div class="bg-[#1a1a1a] rounded-2xl border border-white/5 p-6 group relative"> <div class="bg-[#1a1a1a] rounded-2xl border border-white/5 p-6 group relative">
@@ -39,7 +39,7 @@
<%# Informações do banco externo %> <%# Informações do banco externo %>
<div class="bg-[#1a1a1a] rounded-2xl border border-white/5 p-6"> <div class="bg-[#1a1a1a] rounded-2xl border border-white/5 p-6">
<h2 class="text-lg font-semibold text-white mb-4">🗄️ Banco de Dados Externo</h2> <h2 class="text-lg font-semibold text-white mb-4 flex items-center gap-2"><%= icone :arquivar %> Banco de Dados Externo</h2>
<div class="grid grid-cols-1 sm:grid-cols-3 gap-4"> <div class="grid grid-cols-1 sm:grid-cols-3 gap-4">
<div class="p-4 bg-[#0a0a0a] rounded-xl border border-white/5"> <div class="p-4 bg-[#0a0a0a] rounded-xl border border-white/5">
<p class="text-gray-400 text-xs mb-1">Tabela monitorada</p> <p class="text-gray-400 text-xs mb-1">Tabela monitorada</p>

View File

@@ -20,8 +20,8 @@
<%# Aviso de sincronização %> <%# Aviso de sincronização %>
<div class="p-4 bg-amber-900/20 border border-amber-500/40 rounded-xl text-amber-200/90 text-sm flex items-start gap-2"> <div class="p-4 bg-amber-900/20 border border-amber-500/40 rounded-xl text-amber-200/90 text-sm flex items-start gap-2">
<span class="text-base leading-none">⚠️</span> <%= icone :alerta, cor: nil, tamanho: 'w-4 h-4 mt-0.5' %>
<p>As alterações são gravadas no <strong>SimpliRoute na hora</strong> (cada campo salva ao confirmar ), <p>As alterações são gravadas no <strong>SimpliRoute na hora</strong> (cada campo salva ao confirmar <%= icone :confirmar, cor: nil, tamanho: 'w-3 h-3' %>),
mas o painel e as consolidações só refletem <strong>após a próxima sincronização</strong> da base de mas o painel e as consolidações só refletem <strong>após a próxima sincronização</strong> da base de
rastreio. Toda alteração fica registrada na auditoria.</p> rastreio. Toda alteração fica registrada na auditoria.</p>
</div> </div>
@@ -57,15 +57,15 @@
<div class="flex flex-wrap gap-2 mt-2 text-xs text-gray-500"> <div class="flex flex-wrap gap-2 mt-2 text-xs text-gray-500">
<span class="px-2 py-0.5 rounded-full bg-white/5">NF <span id="c-nf">—</span></span> <span class="px-2 py-0.5 rounded-full bg-white/5">NF <span id="c-nf">—</span></span>
<span class="px-2 py-0.5 rounded-full bg-white/5">Visita <span id="c-id">—</span></span> <span class="px-2 py-0.5 rounded-full bg-white/5">Visita <span id="c-id">—</span></span>
<span class="px-2 py-0.5 rounded-full bg-white/5">🚚 <span id="c-motorista">—</span></span> <span class="px-2 py-0.5 rounded-full bg-white/5"><%= icone :caminhao %> <span id="c-motorista">—</span></span>
<span class="px-2 py-0.5 rounded-full bg-white/5">🚗 <span id="c-veiculo">—</span></span> <span class="px-2 py-0.5 rounded-full bg-white/5"><%= icone :veiculo %> <span id="c-veiculo">—</span></span>
<span class="px-2 py-0.5 rounded-full bg-white/5">📞 <span id="c-telefone">—</span></span> <span class="px-2 py-0.5 rounded-full bg-white/5"><%= icone :telefone %> <span id="c-telefone">—</span></span>
</div> </div>
</div> </div>
<button id="btn-historico" type="button" <button id="btn-historico" type="button"
class="px-4 py-2.5 bg-[#1a1a1a] hover:bg-[#252525] border border-white/10 text-gray-200 class="px-4 py-2.5 bg-[#1a1a1a] hover:bg-[#252525] border border-white/10 text-gray-200
text-sm font-semibold rounded-xl transition-colors whitespace-nowrap"> text-sm font-semibold rounded-xl transition-colors whitespace-nowrap">
🕑 Histórico <%= icone :historico %> Histórico
</button> </button>
</div> </div>
@@ -96,9 +96,9 @@
<div id="modal-hist-overlay" class="absolute inset-0 bg-black/70"></div> <div id="modal-hist-overlay" class="absolute inset-0 bg-black/70"></div>
<div class="relative bg-[#151515] border border-white/10 rounded-2xl w-full max-w-2xl max-h-[85vh] flex flex-col"> <div class="relative bg-[#151515] border border-white/10 rounded-2xl w-full max-w-2xl max-h-[85vh] flex flex-col">
<div class="flex items-center justify-between p-4 border-b border-white/5"> <div class="flex items-center justify-between p-4 border-b border-white/5">
<h3 class="text-white font-bold">🕑 Histórico de alterações</h3> <h3 class="text-white font-bold flex items-center gap-2"><%= icone :historico %> Histórico de alterações</h3>
<button id="modal-hist-fechar" type="button" <button id="modal-hist-fechar" type="button"
class="w-9 h-9 rounded-lg bg-white/5 hover:bg-white/10 text-gray-300"></button> class="w-9 h-9 rounded-lg bg-white/5 hover:bg-white/10 text-gray-300" aria-label="Fechar"><%= icone :fechar, cor: nil, tamanho: 'w-4 h-4' %></button>
</div> </div>
<div id="modal-hist-corpo" class="p-4 overflow-y-auto space-y-5 text-sm"> <div id="modal-hist-corpo" class="p-4 overflow-y-auto space-y-5 text-sm">
<p class="text-gray-400">Carregando…</p> <p class="text-gray-400">Carregando…</p>
@@ -121,12 +121,18 @@
let visita = null; // estado atual do lançamento carregado let visita = null; // estado atual do lançamento carregado
// ── Config dos campos editáveis ───────────────────────────── // ── Config dos campos editáveis ─────────────────────────────
// Ícone do sprite (public/icons.svg). fill=currentColor -> herda a cor do badge.
function ico(id, cls) {
return '<svg class="' + (cls || 'w-3.5 h-3.5') + '" fill="currentColor" ' +
'style="display:inline-block;vertical-align:-2px"><use href="/icons.svg#' + id + '"></use></svg>';
}
const STATUS = { const STATUS = {
completed: { label: 'Completa', cls: 'bg-green-600 text-white', icone: '' }, completed: { label: 'Completa', cls: 'bg-green-600 text-white', icone: 'check-circle-fill' },
failed: { label: 'Falha', cls: 'bg-red-600 text-white', icone: '' }, failed: { label: 'Falha', cls: 'bg-red-600 text-white', icone: 'x-circle-fill' },
pending: { label: 'Pendente', cls: 'bg-yellow-500 text-black', icone: '🕗' }, pending: { label: 'Pendente', cls: 'bg-yellow-500 text-black', icone: 'clock-fill' },
partial: { label: 'Parcial', cls: 'bg-blue-600 text-white', icone: '◐' }, partial: { label: 'Parcial', cls: 'bg-blue-600 text-white', icone: 'hourglass-split' },
canceled: { label: 'Cancelada', cls: 'bg-gray-700 text-gray-300', icone: '🚫' } canceled: { label: 'Cancelada', cls: 'bg-gray-700 text-gray-300', icone: 'slash-circle' }
}; };
const CAMPOS = [ const CAMPOS = [
{ campo: 'status', label: 'Status', tipo: 'status' }, { campo: 'status', label: 'Status', tipo: 'status' },
@@ -190,10 +196,10 @@
} }
function renderBadge() { function renderBadge() {
const cfg = STATUS[visita.status] || { label: visita.status || '—', cls: 'bg-gray-800 text-gray-400', icone: '' }; const cfg = STATUS[visita.status] || { label: visita.status || '—', cls: 'bg-gray-800 text-gray-400', icone: 'question-circle' };
const b = document.getElementById('c-badge'); const b = document.getElementById('c-badge');
b.className = 'inline-flex items-center gap-1 px-2.5 py-1 rounded-full text-xs font-bold ' + cfg.cls; b.className = 'inline-flex items-center gap-1 px-2.5 py-1 rounded-full text-xs font-bold ' + cfg.cls;
b.textContent = cfg.icone + ' ' + cfg.label; b.innerHTML = ico(cfg.icone) + ' ' + esc(cfg.label);
} }
function renderFotos() { function renderFotos() {
@@ -207,7 +213,7 @@
} else { } else {
wrap.innerHTML = wrap.innerHTML =
'<div class="w-full h-52 rounded-xl border border-dashed border-white/10 flex items-center justify-center ' + '<div class="w-full h-52 rounded-xl border border-dashed border-white/10 flex items-center justify-center ' +
'text-gray-600 text-sm">📷 Sem foto da fachada para esta entrega</div>'; 'text-gray-600 text-sm">' + ico('camera-fill') + ' Sem foto da fachada para esta entrega</div>';
} }
const extras = [...(visita.pictures || [])]; const extras = [...(visita.pictures || [])];
@@ -257,7 +263,7 @@
'<p class="text-xs text-gray-500 mb-0.5">' + esc(def.label) + '</p>' + '<p class="text-xs text-gray-500 mb-0.5">' + esc(def.label) + '</p>' +
'<div class="flex items-start justify-between gap-2">' + '<div class="flex items-start justify-between gap-2">' +
'<p class="texto-valor text-sm text-white break-words min-h-[20px]">' + esc(valorExibicao(def)) + '</p>' + '<p class="texto-valor text-sm text-white break-words min-h-[20px]">' + esc(valorExibicao(def)) + '</p>' +
'<span class="text-gray-600 group-hover:text-orange-400 text-sm" title="Editar">✏️</span>' + '<span class="text-gray-600 group-hover:text-orange-400" title="Editar">' + ico('pencil-fill') + '</span>' +
'</div>'; '</div>';
div.onclick = () => abrirEdicao(div, def); div.onclick = () => abrirEdicao(div, def);
if (flash) { if (flash) {
@@ -297,8 +303,8 @@
'<p class="text-xs text-orange-400 mb-1">' + esc(def.label) + '</p>' + '<p class="text-xs text-orange-400 mb-1">' + esc(def.label) + '</p>' +
editorHtml + editorHtml +
'<div class="flex items-center gap-2 mt-2">' + '<div class="flex items-center gap-2 mt-2">' +
'<button type="button" class="btn-ok px-3 py-1.5 bg-green-600 hover:bg-green-500 text-white text-xs font-bold rounded-lg"> Salvar</button>' + '<button type="button" class="btn-ok px-3 py-1.5 bg-green-600 hover:bg-green-500 text-white text-xs font-bold rounded-lg">' + ico('check-lg', 'w-3 h-3') + ' Salvar</button>' +
'<button type="button" class="btn-cancelar px-3 py-1.5 bg-white/5 hover:bg-white/10 text-gray-300 text-xs rounded-lg"> Cancelar</button>' + '<button type="button" class="btn-cancelar px-3 py-1.5 bg-white/5 hover:bg-white/10 text-gray-300 text-xs rounded-lg">' + ico('x-lg', 'w-3 h-3') + ' Cancelar</button>' +
'<span class="msg-salvando hidden text-xs text-gray-400">Salvando…</span>' + '<span class="msg-salvando hidden text-xs text-gray-400">Salvando…</span>' +
'</div>' + '</div>' +
'<p class="msg-erro hidden mt-1.5 text-xs text-red-400"></p>'; '<p class="msg-erro hidden mt-1.5 text-xs text-red-400"></p>';
@@ -388,7 +394,7 @@
function htmlHistorico(data) { function htmlHistorico(data) {
let html = '<div>' + let html = '<div>' +
'<p class="text-xs font-semibold text-gray-500 uppercase tracking-wider mb-2">📒 Nosso sistema (auditoria)</p>'; '<p class="text-xs font-semibold text-gray-500 uppercase tracking-wider mb-2">' + ico('journal-text') + ' Nosso sistema (auditoria)</p>';
const interno = (data.interno || []); const interno = (data.interno || []);
if (!interno.length) { if (!interno.length) {
html += '<p class="text-gray-500">Nenhuma alteração registrada pelo sistema.</p>'; html += '<p class="text-gray-500">Nenhuma alteração registrada pelo sistema.</p>';
@@ -400,14 +406,14 @@
' → <span class="text-green-300">' + esc(fmtValor(campo, l.para[campo])) + '</span></li>').join(''); ' → <span class="text-green-300">' + esc(fmtValor(campo, l.para[campo])) + '</span></li>').join('');
return '<div class="bg-[#1a1a1a] border border-white/5 rounded-xl p-3 mb-2">' + return '<div class="bg-[#1a1a1a] border border-white/5 rounded-xl p-3 mb-2">' +
'<div class="flex items-center justify-between text-xs text-gray-500 mb-1.5">' + '<div class="flex items-center justify-between text-xs text-gray-500 mb-1.5">' +
'<span>👤 ' + esc(l.usuario) + '</span><span>' + esc(l.quando) + '</span>' + '<span>' + ico('person-fill') + ' ' + esc(l.usuario) + '</span><span>' + esc(l.quando) + '</span>' +
'</div>' + '</div>' +
'<ul class="space-y-0.5 text-xs">' + (mudancas || '<li class="text-gray-500">—</li>') + '</ul>' + '<ul class="space-y-0.5 text-xs">' + (mudancas || '<li class="text-gray-500">—</li>') + '</ul>' +
'</div>'; '</div>';
}).join(''); }).join('');
} }
html += '</div><div>' + html += '</div><div>' +
'<p class="text-xs font-semibold text-gray-500 uppercase tracking-wider mb-2">🌐 SimpliRoute (API)</p>'; '<p class="text-xs font-semibold text-gray-500 uppercase tracking-wider mb-2">' + ico('globe2') + ' SimpliRoute (API)</p>';
const api = (data.api || []); const api = (data.api || []);
if (!api.length) { if (!api.length) {
html += '<p class="text-gray-500">Sem registros de histórico na API do SimpliRoute.</p>'; html += '<p class="text-gray-500">Sem registros de histórico na API do SimpliRoute.</p>';

View File

@@ -43,7 +43,7 @@
<button type="submit" <button type="submit"
class="px-6 py-3 bg-[#f97316] hover:bg-orange-500 text-white font-semibold rounded-xl class="px-6 py-3 bg-[#f97316] hover:bg-orange-500 text-white font-semibold rounded-xl
transition-colors min-h-[48px] whitespace-nowrap"> transition-colors min-h-[48px] whitespace-nowrap">
📥 Baixar planilha <%= icone :baixar, cor: nil %> Baixar planilha
</button> </button>
<% end %> <% end %>
<% end %> <% end %>

View File

@@ -103,7 +103,7 @@
<% if @usuarios.empty? %> <% if @usuarios.empty? %>
<tr> <tr>
<td colspan="5" class="px-6 py-16 text-center text-gray-500"> <td colspan="5" class="px-6 py-16 text-center text-gray-500">
<div class="text-4xl mb-3">👥</div> <div class="mb-3"><%= icone :usuarios, tamanho: 'w-10 h-10', cor: 'text-gray-600' %></div>
<p>Nenhum usuário encontrado.</p> <p>Nenhum usuário encontrado.</p>
</td> </td>
</tr> </tr>

View File

@@ -10,7 +10,7 @@
<%# Pilares de preço %> <%# Pilares de preço %>
<div> <div>
<h2 class="text-lg font-semibold text-white mb-4">💰 Pilares de Preço</h2> <h2 class="text-lg font-semibold text-white mb-4 flex items-center gap-2"><%= icone :dinheiro %> Pilares de Preço</h2>
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4"> <div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4">
<% @configuracoes.each do |cfg| %> <% @configuracoes.each do |cfg| %>
<div class="bg-[#1a1a1a] rounded-2xl border border-white/5 p-6 group relative"> <div class="bg-[#1a1a1a] rounded-2xl border border-white/5 p-6 group relative">
@@ -39,7 +39,7 @@
<%# Informações do banco externo %> <%# Informações do banco externo %>
<div class="bg-[#1a1a1a] rounded-2xl border border-white/5 p-6"> <div class="bg-[#1a1a1a] rounded-2xl border border-white/5 p-6">
<h2 class="text-lg font-semibold text-white mb-4">🗄️ Banco de Dados Externo</h2> <h2 class="text-lg font-semibold text-white mb-4 flex items-center gap-2"><%= icone :arquivar %> Banco de Dados Externo</h2>
<div class="grid grid-cols-1 sm:grid-cols-3 gap-4"> <div class="grid grid-cols-1 sm:grid-cols-3 gap-4">
<div class="p-4 bg-[#0a0a0a] rounded-xl border border-white/5"> <div class="p-4 bg-[#0a0a0a] rounded-xl border border-white/5">
<p class="text-gray-400 text-xs mb-1">Tabela monitorada</p> <p class="text-gray-400 text-xs mb-1">Tabela monitorada</p>

View File

@@ -4,10 +4,10 @@
<div class="max-w-3xl mx-auto"> <div class="max-w-3xl mx-auto">
<div class="mb-6"> <div class="mb-6">
<%= link_to 'Voltar (Passo 2)', <%= link_to rotulo(:voltar, 'Voltar (Passo 2)', cor: nil),
validar_consolidacao_consolidacao_entregas_path(@consolidacao, motorista: @motorista), validar_consolidacao_consolidacao_entregas_path(@consolidacao, motorista: @motorista),
class: 'inline-flex items-center gap-1 bg-[#1a1a1a] hover:bg-[#2a2a2a] text-white border border-[#2a2a2a] hover:border-orange-500 font-semibold px-4 py-2.5 rounded-lg text-sm min-h-[44px]' %> class: 'inline-flex items-center gap-1 bg-[#1a1a1a] hover:bg-[#2a2a2a] text-white border border-[#2a2a2a] hover:border-orange-500 font-semibold px-4 py-2.5 rounded-lg text-sm min-h-[44px]' %>
<h1 class="text-2xl font-bold text-white mt-2">📋 Revisão — <%= @motorista %></h1> <h1 class="text-2xl font-bold text-white mt-2 flex items-center gap-2"><%= icone :planilha %> Revisão — <%= @motorista %></h1>
<p class="text-gray-400 text-sm"><%= @consolidacao.nome %></p> <p class="text-gray-400 text-sm"><%= @consolidacao.nome %></p>
</div> </div>
@@ -71,19 +71,20 @@
<%= c.tipo_cor[:label] %><% if (c.termo? || c.termo_especial?) && c.quantidade > 1 %> ×<%= c.quantidade %><% end %> <%= c.tipo_cor[:label] %><% if (c.termo? || c.termo_especial?) && c.quantidade > 1 %> ×<%= c.quantidade %><% end %>
</span> </span>
<% if c.manual? %> <% if c.manual? %>
<span class="ml-1 bg-orange-500/15 text-orange-400 border border-orange-500/40 px-2 py-1 rounded text-xs font-bold"> Apontamento</span> <span class="ml-1 bg-orange-500/15 text-orange-400 border border-orange-500/40 px-2 py-1 rounded text-xs font-bold"><%= icone :adicionar, cor: nil, tamanho: 'w-3 h-3' %> Apontamento</span>
<% end %> <% end %>
</td> </td>
<td class="px-4 py-3 text-right font-bold <%= c.desconto? ? 'text-red-400' : 'text-white' %>"> <td class="px-4 py-3 text-right font-bold <%= c.desconto? ? 'text-red-400' : 'text-white' %>">
<div class="flex items-center justify-end gap-2"> <div class="flex items-center justify-end gap-2">
<span><%= c.desconto? ? '-' : '' %><%= moeda(c.valor_aplicado) %></span> <span><%= c.desconto? ? '-' : '' %><%= moeda(c.valor_aplicado) %></span>
<% if c.manual? && !(@consolidacao.finalizada? && current_user.operador?) %> <% if c.manual? && !(@consolidacao.finalizada? && current_user.operador?) %>
<%= button_to '✕', <%= button_to remover_apontamento_consolidacao_consolidacao_entregas_path(@consolidacao, id: c.id, motorista: @motorista),
remover_apontamento_consolidacao_consolidacao_entregas_path(@consolidacao, id: c.id, motorista: @motorista),
method: :delete, method: :delete,
form: { data: { turbo_confirm: 'Remover este apontamento manual?' } }, form: { data: { turbo_confirm: 'Remover este apontamento manual?' } },
class: 'text-gray-500 hover:text-red-400 text-sm leading-none px-1', class: 'text-gray-500 hover:text-red-400 leading-none px-1',
title: 'Remover apontamento' %> title: 'Remover apontamento' do %>
<%= icone :fechar, cor: nil, tamanho: 'w-3.5 h-3.5' %>
<% end %>
<% end %> <% end %>
</div> </div>
</td> </td>
@@ -127,11 +128,11 @@
<%# Ações finais %> <%# Ações finais %>
<div class="flex flex-col md:flex-row gap-3"> <div class="flex flex-col md:flex-row gap-3">
<% if @proximo %> <% if @proximo %>
<%= link_to "Próximo motorista: #{@proximo}", <%= link_to safe_join(["Próximo motorista: #{@proximo}", icone(:avancar, cor: nil)], ' '),
validar_consolidacao_consolidacao_entregas_path(@consolidacao, motorista: @proximo), validar_consolidacao_consolidacao_entregas_path(@consolidacao, motorista: @proximo),
class: 'flex-1 bg-orange-500 hover:bg-orange-600 text-black font-bold py-3.5 rounded-lg min-h-[48px] flex items-center justify-center text-center' %> class: 'flex-1 bg-orange-500 hover:bg-orange-600 text-black font-bold py-3.5 rounded-lg min-h-[48px] flex items-center justify-center text-center' %>
<% end %> <% end %>
<%= link_to '💾 Salvar rascunho e voltar', wizard_consolidacao_path(@consolidacao), <%= link_to rotulo(:salvar, 'Salvar rascunho e voltar', cor: nil), wizard_consolidacao_path(@consolidacao),
class: 'flex-1 bg-[#1a1a1a] hover:bg-[#2a2a2a] text-white border border-[#2a2a2a] font-bold py-3.5 rounded-lg min-h-[48px] flex items-center justify-center' %> class: 'flex-1 bg-[#1a1a1a] hover:bg-[#2a2a2a] text-white border border-[#2a2a2a] font-bold py-3.5 rounded-lg min-h-[48px] flex items-center justify-center' %>
</div> </div>
</div> </div>

View File

@@ -18,10 +18,10 @@
class: 'inline-flex items-center gap-1 bg-[#1a1a1a] hover:bg-[#2a2a2a] text-white border border-[#2a2a2a] hover:border-orange-500 font-semibold px-4 py-2.5 rounded-lg text-sm min-h-[44px]' %> class: 'inline-flex items-center gap-1 bg-[#1a1a1a] hover:bg-[#2a2a2a] text-white border border-[#2a2a2a] hover:border-orange-500 font-semibold px-4 py-2.5 rounded-lg text-sm min-h-[44px]' %>
<div class="flex items-center justify-between mt-2"> <div class="flex items-center justify-between mt-2">
<div> <div>
<h1 class="text-2xl font-bold text-white">🚚 <%= @motorista %></h1> <h1 class="text-2xl font-bold text-white flex items-center gap-2"><%= icone :caminhao %> <%= @motorista %></h1>
<p class="text-gray-400 text-sm"><%= @consolidacao.nome %> · <%= l @consolidacao.data_inicio, format: :short %> → <%= l @consolidacao.data_fim, format: :short %></p> <p class="text-gray-400 text-sm"><%= @consolidacao.nome %> · <%= l @consolidacao.data_inicio, format: :short %> → <%= l @consolidacao.data_fim, format: :short %></p>
</div> </div>
<%= link_to 'Revisar (Passo 3)', <%= link_to safe_join(['Revisar (Passo 3)', icone(:avancar, cor: nil)], ' '),
revisar_consolidacao_consolidacao_entregas_path(@consolidacao, motorista: @motorista), revisar_consolidacao_consolidacao_entregas_path(@consolidacao, motorista: @motorista),
class: 'bg-orange-500 hover:bg-orange-600 text-black font-bold px-5 py-3 rounded-lg min-h-[48px] flex items-center' %> class: 'bg-orange-500 hover:bg-orange-600 text-black font-bold px-5 py-3 rounded-lg min-h-[48px] flex items-center' %>
</div> </div>
@@ -31,7 +31,7 @@
<div class="bg-[#1a1a1a] border border-[#2a2a2a] rounded-xl p-3 mb-3"> <div class="bg-[#1a1a1a] border border-[#2a2a2a] rounded-xl p-3 mb-3">
<div class="flex justify-between text-sm mb-2"> <div class="flex justify-between text-sm mb-2">
<span class="text-white font-semibold"> <span class="text-white font-semibold">
<% if @veiculos_sel.any? %>🚚 <%= @veiculos_sel.join(', ') %> — <% end %> <% if @veiculos_sel.any? %><%= icone :caminhao %> <%= @veiculos_sel.join(', ') %> — <% end %>
<span data-validacao-target="contadorClassificadas"><%= @classificadas %></span> <span data-validacao-target="contadorClassificadas"><%= @classificadas %></span>
de <span data-validacao-target="contadorTotal"><%= @total_entregas %></span> entregas classificadas de <span data-validacao-target="contadorTotal"><%= @total_entregas %></span> entregas classificadas
<span data-validacao-target="manuaisInfo" class="<%= 'hidden' unless @manuais.positive? %> text-blue-400 font-normal">· <span data-validacao-target="manuaisInfo" class="<%= 'hidden' unless @manuais.positive? %> text-blue-400 font-normal">·
@@ -53,8 +53,8 @@
<% if @veiculos_status.size > 1 %> <% if @veiculos_status.size > 1 %>
<div class="bg-[#1a1a1a] border border-[#2a2a2a] rounded-xl p-3 mb-3"> <div class="bg-[#1a1a1a] border border-[#2a2a2a] rounded-xl p-3 mb-3">
<div class="flex items-center gap-2 mb-2 flex-wrap"> <div class="flex items-center gap-2 mb-2 flex-wrap">
<span class="text-gray-400 text-sm font-semibold">🚗 Fechar por veículo:</span> <span class="text-gray-400 text-sm font-semibold"><%= icone :veiculo %> Fechar por veículo:</span>
<span class="text-gray-600 text-xs">clique no nome para filtrar; consolide o carro quando estiver 100% (🔒)</span> <span class="text-gray-600 text-xs">clique no nome para filtrar; consolide o carro quando estiver 100% (<%= icone :travado, cor: nil, tamanho: 'w-3 h-3' %>)</span>
</div> </div>
<div class="flex flex-wrap gap-2"> <div class="flex flex-wrap gap-2">
<%# Chip "Todos" — limpa a seleção (mostra todos os carros) %> <%# Chip "Todos" — limpa a seleção (mostra todos os carros) %>
@@ -68,7 +68,7 @@
<% selecionado = @veiculos_sel.include?(v[:vehicle]) %> <% selecionado = @veiculos_sel.include?(v[:vehicle]) %>
<%# Alterna este veículo dentro da seleção atual (multi-seleção) %> <%# Alterna este veículo dentro da seleção atual (multi-seleção) %>
<% nova_sel = selecionado ? (@veiculos_sel - [v[:vehicle]]) : (@veiculos_sel + [v[:vehicle]]) %> <% nova_sel = selecionado ? (@veiculos_sel - [v[:vehicle]]) : (@veiculos_sel + [v[:vehicle]]) %>
<%# Cor determinística: selecionado(laranja) > consolidado(verde+🔒) > <%# Cor determinística: selecionado(laranja) > consolidado(verde+cadeado) >
pronto(verde) > arquivado(cinza tracejado) > neutro. %> pronto(verde) > arquivado(cinza tracejado) > neutro. %>
<% cor = if selecionado then 'border-orange-500 text-orange-400 bg-orange-500/10' <% cor = if selecionado then 'border-orange-500 text-orange-400 bg-orange-500/10'
elsif v[:fechado] then 'border-green-500 text-green-400 bg-green-500/10' elsif v[:fechado] then 'border-green-500 text-green-400 bg-green-500/10'
@@ -78,11 +78,11 @@
<%= link_to validar_consolidacao_consolidacao_entregas_path(@consolidacao, motorista: @motorista, vehicle: nova_sel.presence), <%= link_to validar_consolidacao_consolidacao_entregas_path(@consolidacao, motorista: @motorista, vehicle: nova_sel.presence),
data: { 'validacao-target': 'chipVeiculo', vehicle: v[:vehicle], eligible: v[:eligible] }, data: { 'validacao-target': 'chipVeiculo', vehicle: v[:vehicle], eligible: v[:eligible] },
class: "px-3 py-2 rounded-lg text-sm font-semibold border min-h-[40px] flex items-center gap-2 transition-colors #{cor}" do %> class: "px-3 py-2 rounded-lg text-sm font-semibold border min-h-[40px] flex items-center gap-2 transition-colors #{cor}" do %>
<span data-chip-lock class="<%= 'hidden' unless v[:fechado] %>">🔒</span> <span data-chip-lock class="<%= 'hidden' unless v[:fechado] %>"><%= icone :travado, cor: nil, tamanho: 'w-3 h-3' %></span>
<span><%= v[:vehicle] %></span> <span><%= v[:vehicle] %></span>
<span class="text-xs <%= v[:completo] ? 'text-green-400' : 'text-gray-500' %>"> <span class="text-xs <%= v[:completo] ? 'text-green-400' : 'text-gray-500' %>">
<span data-chip-feito><%= v[:classificadas] %></span>/<span data-chip-total><%= v[:eligible] %></span> <span data-chip-feito><%= v[:classificadas] %></span>/<span data-chip-total><%= v[:eligible] %></span>
<span data-chip-check><%= '✓' if v[:completo] %></span> <span data-chip-check class="<%= 'hidden' unless v[:completo] %>"><%= icone :confirmar, cor: nil, tamanho: 'w-3 h-3' %></span>
</span> </span>
<% end %> <% end %>
<% end %> <% end %>
@@ -97,32 +97,35 @@
<p class="text-gray-500 text-xs">Selecione <strong>um</strong> veículo (clique no nome) para consolidá-lo separadamente.</p> <p class="text-gray-500 text-xs">Selecione <strong>um</strong> veículo (clique no nome) para consolidá-lo separadamente.</p>
<% elsif status_unico[:fechado] %> <% elsif status_unico[:fechado] %>
<div class="flex items-center gap-2 flex-wrap"> <div class="flex items-center gap-2 flex-wrap">
<span class="text-green-400 text-sm font-semibold">🔒 <%= sel_unico %> consolidado.</span> <span class="text-green-400 text-sm font-semibold"><%= icone :travado, cor: nil %> <%= sel_unico %> consolidado.</span>
<%= button_to '🔓 Reabrir veículo', <%= button_to reabrir_veiculo_consolidacao_consolidacao_entregas_path(@consolidacao),
reabrir_veiculo_consolidacao_consolidacao_entregas_path(@consolidacao),
method: :post, method: :post,
params: { motorista: @motorista, vehicle: sel_unico, vehicle_sel: @veiculos_sel }, params: { motorista: @motorista, vehicle: sel_unico, vehicle_sel: @veiculos_sel },
data: { turbo_confirm: "Reabrir o veículo #{sel_unico} para edição?" }, form: { data: { turbo_confirm: "Reabrir o veículo #{sel_unico} para edição?" } },
class: 'text-gray-400 hover:text-white border border-[#2a2a2a] hover:border-orange-500 px-3 py-2 rounded-lg text-xs min-h-[40px]' %> class: 'text-gray-400 hover:text-white border border-[#2a2a2a] hover:border-orange-500 px-3 py-2 rounded-lg text-xs min-h-[40px] inline-flex items-center gap-1.5' do %>
<%= icone :destravado, cor: nil, tamanho: 'w-3.5 h-3.5' %> Reabrir veículo
<% end %>
</div> </div>
<% else %> <% else %>
<div class="flex items-center gap-2 flex-wrap"> <div class="flex items-center gap-2 flex-wrap">
<%# Habilita só quando o carro está 100% classificado; o JS liga/desliga <%# Habilita só quando o carro está 100% classificado; o JS liga/desliga
conforme a barra do escopo selecionado chega (ou não) a 100%. %> conforme a barra do escopo selecionado chega (ou não) a 100%. %>
<%= button_to '🔒 Consolidar este veículo', <%= button_to fechar_veiculo_consolidacao_consolidacao_entregas_path(@consolidacao),
fechar_veiculo_consolidacao_consolidacao_entregas_path(@consolidacao),
method: :post, method: :post,
params: { motorista: @motorista, vehicle: sel_unico, vehicle_sel: @veiculos_sel }, params: { motorista: @motorista, vehicle: sel_unico, vehicle_sel: @veiculos_sel },
disabled: !status_unico[:completo], disabled: !status_unico[:completo],
data: { 'validacao-target': 'consolidarBtn', data: { 'validacao-target': 'consolidarBtn' },
turbo_confirm: "Consolidar o veículo #{sel_unico}? Você poderá reabrir depois." }, form: { data: { turbo_confirm: "Consolidar o veículo #{sel_unico}? Você poderá reabrir depois." } },
class: "font-bold px-4 py-2 rounded-lg text-xs min-h-[40px] #{status_unico[:completo] ? 'bg-green-600 hover:bg-green-500 text-white cursor-pointer' : 'bg-[#2a2a2a] text-gray-600 cursor-not-allowed'}" %> class: "font-bold px-4 py-2 rounded-lg text-xs min-h-[40px] inline-flex items-center gap-1.5 #{status_unico[:completo] ? 'bg-green-600 hover:bg-green-500 text-white cursor-pointer' : 'bg-[#2a2a2a] text-gray-600 cursor-not-allowed'}" do %>
<%= button_to '🗄️ Arquivar veículo', <%= icone :travado, cor: nil, tamanho: 'w-3.5 h-3.5' %> Consolidar este veículo
arquivar_veiculo_consolidacao_consolidacao_entregas_path(@consolidacao), <% end %>
<%= button_to arquivar_veiculo_consolidacao_consolidacao_entregas_path(@consolidacao),
method: :post, method: :post,
params: { motorista: @motorista, vehicle: sel_unico, vehicle_sel: @veiculos_sel }, params: { motorista: @motorista, vehicle: sel_unico, vehicle_sel: @veiculos_sel },
data: { turbo_confirm: "Arquivar o veículo #{sel_unico}? Ele sai da finalização (as entregas já classificadas continuam no pagamento)." }, form: { data: { turbo_confirm: "Arquivar o veículo #{sel_unico}? Ele sai da finalização (as entregas já classificadas continuam no pagamento)." } },
class: 'text-gray-400 hover:text-white border border-[#2a2a2a] hover:border-red-500/50 px-3 py-2 rounded-lg text-xs min-h-[40px]' %> class: 'text-gray-400 hover:text-white border border-[#2a2a2a] hover:border-red-500/50 px-3 py-2 rounded-lg text-xs min-h-[40px] inline-flex items-center gap-1.5' do %>
<%= icone :arquivar, cor: nil, tamanho: 'w-3.5 h-3.5' %> Arquivar veículo
<% end %>
<span class="text-gray-500 text-xs" data-validacao-target="consolidarHint"> <span class="text-gray-500 text-xs" data-validacao-target="consolidarHint">
<%= status_unico[:completo] ? 'Pronto para consolidar.' : 'Classifique 100% deste veículo para consolidar.' %> <%= status_unico[:completo] ? 'Pronto para consolidar.' : 'Classifique 100% deste veículo para consolidar.' %>
</span> </span>
@@ -160,7 +163,7 @@
<%# Oculta/mostra o resumo lateral (mais largura para a lista) %> <%# Oculta/mostra o resumo lateral (mais largura para a lista) %>
<button type="button" data-action="validacao#toggleResumo" <button type="button" data-action="validacao#toggleResumo"
class="text-gray-400 hover:text-white text-sm border border-[#2a2a2a] hover:border-orange-500 px-3 py-2 rounded-lg whitespace-nowrap"> class="text-gray-400 hover:text-white text-sm border border-[#2a2a2a] hover:border-orange-500 px-3 py-2 rounded-lg whitespace-nowrap">
📊 <span data-validacao-target="btnResumoLabel">Ocultar resumo</span> <%= icone :dashboard %> <span data-validacao-target="btnResumoLabel">Ocultar resumo</span>
</button> </button>
<%= link_to params[:apenas_pendentes] == '1' ? 'Mostrar todas' : 'Apenas não classificadas', <%= link_to params[:apenas_pendentes] == '1' ? 'Mostrar todas' : 'Apenas não classificadas',
validar_consolidacao_consolidacao_entregas_path(@consolidacao, motorista: @motorista, vehicle: @veiculos_sel.presence, validar_consolidacao_consolidacao_entregas_path(@consolidacao, motorista: @motorista, vehicle: @veiculos_sel.presence,
@@ -182,20 +185,20 @@
<%# Botão único + menu suspenso %> <%# Botão único + menu suspenso %>
<button type="button" data-action="ferramentas#toggle" <button type="button" data-action="ferramentas#toggle"
class="w-full md:w-auto inline-flex items-center justify-center gap-2 bg-[#1a1a1a] hover:bg-[#2a2a2a] text-white border border-dashed border-[#3a3a3a] hover:border-orange-500 font-semibold px-4 py-3 rounded-lg min-h-[48px]"> class="w-full md:w-auto inline-flex items-center justify-center gap-2 bg-[#1a1a1a] hover:bg-[#2a2a2a] text-white border border-dashed border-[#3a3a3a] hover:border-orange-500 font-semibold px-4 py-3 rounded-lg min-h-[48px]">
Adicionar lançamento <%= icone :adicionar %> Adicionar lançamento
<span class="text-gray-500">▾</span> <%= icone :expandir, cor: 'text-gray-500' %>
</button> </button>
<div data-ferramentas-target="menu" <div data-ferramentas-target="menu"
class="hidden absolute z-40 mt-2 w-72 bg-[#1a1a1a] border border-[#2a2a2a] rounded-xl shadow-xl overflow-hidden"> class="hidden absolute z-40 mt-2 w-72 bg-[#1a1a1a] border border-[#2a2a2a] rounded-xl shadow-xl overflow-hidden">
<button type="button" data-action="apontamento-manual#abrir ferramentas#fechar" <button type="button" data-action="apontamento-manual#abrir ferramentas#fechar"
class="w-full text-left px-4 py-3 hover:bg-[#2a2a2a] border-l-2 border-transparent hover:border-purple-500"> class="w-full text-left px-4 py-3 hover:bg-[#2a2a2a] border-l-2 border-transparent hover:border-purple-500">
<span class="block text-white font-semibold">📝 Registrar entrega manualmente</span> <span class="block text-white font-semibold"><%= icone :apontamento %> Registrar entrega manualmente</span>
<span class="block text-gray-500 text-xs">fora da base de rastreio</span> <span class="block text-gray-500 text-xs">fora da base de rastreio</span>
</button> </button>
<button type="button" data-action="apontamento-termo#abrir ferramentas#fechar" <button type="button" data-action="apontamento-termo#abrir ferramentas#fechar"
class="w-full text-left px-4 py-3 hover:bg-[#2a2a2a] border-t border-[#2a2a2a] border-l-2 border-l-transparent hover:border-l-blue-500"> class="w-full text-left px-4 py-3 hover:bg-[#2a2a2a] border-t border-[#2a2a2a] border-l-2 border-l-transparent hover:border-l-blue-500">
<span class="block text-white font-semibold">📄 Entrega de termo</span> <span class="block text-white font-semibold"><%= icone :documento %> Entrega de termo</span>
<span class="block text-gray-500 text-xs">sem nota/código — só quantidade</span> <span class="block text-gray-500 text-xs">sem nota/código — só quantidade</span>
</button> </button>
</div> </div>
@@ -207,7 +210,7 @@
<div class="flex items-center justify-between mb-1"> <div class="flex items-center justify-between mb-1">
<h2 class="text-xl font-bold text-white">Registro manual de entrega</h2> <h2 class="text-xl font-bold text-white">Registro manual de entrega</h2>
<button type="button" data-action="apontamento-manual#fechar" <button type="button" data-action="apontamento-manual#fechar"
class="text-gray-500 hover:text-white text-2xl leading-none"></button> class="text-gray-500 hover:text-white leading-none" aria-label="Fechar"><%= icone :fechar, cor: nil, tamanho: 'w-5 h-5' %></button>
</div> </div>
<p class="text-gray-400 text-sm mb-4">Lançe uma entrega à mão, sem buscar na base de rastreio. Use quando o motorista foi ao local mas a nota ficou registrada com outro motorista.</p> <p class="text-gray-400 text-sm mb-4">Lançe uma entrega à mão, sem buscar na base de rastreio. Use quando o motorista foi ao local mas a nota ficou registrada com outro motorista.</p>
@@ -251,16 +254,16 @@
<%# Pilares (múltiplos) %> <%# Pilares (múltiplos) %>
<label class="block text-gray-400 text-sm mb-2">Classificar como <span class="text-gray-600">(pode marcar mais de uma)</span></label> <label class="block text-gray-400 text-sm mb-2">Classificar como <span class="text-gray-600">(pode marcar mais de uma)</span></label>
<div class="grid grid-cols-2 gap-2 mb-3"> <div class="grid grid-cols-2 gap-2 mb-3">
<% [['entrega_normal', '🚚 Entrega Normal', Configuracao.preco_entrega], <% [['entrega_normal', :caminhao, 'Entrega Normal', Configuracao.preco_entrega],
['retirada', '📦 Retirada', Configuracao.preco_retirada], ['retirada', :retirada, 'Retirada', Configuracao.preco_retirada],
['bonus', 'Bônus', Configuracao.preco_bonus], ['bonus', :bonus, 'Bônus', Configuracao.preco_bonus],
['desconto', '⚠️ Desconto', Configuracao.preco_desconto], ['desconto', :desconto, 'Desconto', Configuracao.preco_desconto],
['extraordinaria', 'Entrega Extraordinária', Configuracao.preco_extraordinaria]].each do |tipo, label, preco| %> ['extraordinaria', :extraordinaria, 'Entrega Extraordinária', Configuracao.preco_extraordinaria]].each do |tipo, ic, label, preco| %>
<label class="flex items-center gap-2 bg-[#0a0a0a] border border-[#2a2a2a] rounded-lg px-3 py-3 cursor-pointer hover:border-purple-500 text-sm text-white"> <label class="flex items-center gap-2 bg-[#0a0a0a] border border-[#2a2a2a] rounded-lg px-3 py-3 cursor-pointer hover:border-purple-500 text-sm text-white">
<input type="checkbox" value="<%= tipo %>" data-apontamento-manual-target="tipoCheck" <input type="checkbox" value="<%= tipo %>" data-apontamento-manual-target="tipoCheck"
<%= 'checked' if tipo == 'entrega_normal' %> <%= 'checked' if tipo == 'entrega_normal' %>
class="accent-purple-500"> class="accent-purple-500">
<span><%= label %><br><span class="text-gray-500 text-xs"><%= moeda(preco) %></span></span> <span><%= icone ic %> <%= label %><br><span class="text-gray-500 text-xs"><%= moeda(preco) %></span></span>
</label> </label>
<% end %> <% end %>
</div> </div>
@@ -274,7 +277,7 @@
<button type="button" data-action="apontamento-manual#adicionar" <button type="button" data-action="apontamento-manual#adicionar"
class="w-full bg-green-600 hover:bg-green-500 text-white font-bold py-3.5 rounded-lg min-h-[48px]"> class="w-full bg-green-600 hover:bg-green-500 text-white font-bold py-3.5 rounded-lg min-h-[48px]">
Adicionar apontamento Adicionar apontamento <%= icone :confirmar, cor: nil %>
</button> </button>
</div> </div>
</div> </div>
@@ -284,9 +287,9 @@
class="hidden fixed inset-0 z-50 bg-black/70 flex items-center justify-center p-4"> class="hidden fixed inset-0 z-50 bg-black/70 flex items-center justify-center p-4">
<div class="bg-[#1a1a1a] border border-[#2a2a2a] rounded-2xl w-full max-w-md p-6"> <div class="bg-[#1a1a1a] border border-[#2a2a2a] rounded-2xl w-full max-w-md p-6">
<div class="flex items-center justify-between mb-1"> <div class="flex items-center justify-between mb-1">
<h2 class="text-xl font-bold text-white">📄 Entrega de termo</h2> <h2 class="text-xl font-bold text-white flex items-center gap-2"><%= icone :documento %> Entrega de termo</h2>
<button type="button" data-action="apontamento-termo#fechar" <button type="button" data-action="apontamento-termo#fechar"
class="text-gray-500 hover:text-white text-2xl leading-none"></button> class="text-gray-500 hover:text-white leading-none" aria-label="Fechar"><%= icone :fechar, cor: nil, tamanho: 'w-5 h-5' %></button>
</div> </div>
<p class="text-gray-400 text-sm mb-4">Entrega sem nota fiscal nem código de rastreio. Informe quantos termos de cada tipo foram entregues.</p> <p class="text-gray-400 text-sm mb-4">Entrega sem nota fiscal nem código de rastreio. Informe quantos termos de cada tipo foram entregues.</p>
@@ -295,7 +298,7 @@
<%# Motorista (contexto da tela) %> <%# Motorista (contexto da tela) %>
<div class="bg-[#0a0a0a] border border-[#2a2a2a] rounded-lg px-4 py-3 mb-4"> <div class="bg-[#0a0a0a] border border-[#2a2a2a] rounded-lg px-4 py-3 mb-4">
<p class="text-gray-500 text-xs">Motorista</p> <p class="text-gray-500 text-xs">Motorista</p>
<p class="text-white font-semibold">🚚 <%= @motorista %></p> <p class="text-white font-semibold"><%= icone :caminhao %> <%= @motorista %></p>
</div> </div>
<%# Veículo (Q3) — a qual carro estes termos pertencem %> <%# Veículo (Q3) — a qual carro estes termos pertencem %>
@@ -311,7 +314,7 @@
<%# Steppers de quantidade — termo normal e termo especial %> <%# Steppers de quantidade — termo normal e termo especial %>
<div class="grid grid-cols-2 gap-3 mb-5"> <div class="grid grid-cols-2 gap-3 mb-5">
<div class="bg-[#0a0a0a] border border-[#2a2a2a] rounded-lg p-3"> <div class="bg-[#0a0a0a] border border-[#2a2a2a] rounded-lg p-3">
<p class="text-white text-sm font-semibold text-center">📄 Termo Normal</p> <p class="text-white text-sm font-semibold text-center"><%= icone :documento %> Termo Normal</p>
<p class="text-blue-400 text-xs text-center mb-2"><%= moeda(@precos[:termo]) %> cada</p> <p class="text-blue-400 text-xs text-center mb-2"><%= moeda(@precos[:termo]) %> cada</p>
<div class="flex items-center justify-center gap-2"> <div class="flex items-center justify-center gap-2">
<button type="button" data-action="apontamento-termo#menos" <button type="button" data-action="apontamento-termo#menos"
@@ -323,7 +326,7 @@
</div> </div>
</div> </div>
<div class="bg-[#0a0a0a] border border-[#2a2a2a] rounded-lg p-3"> <div class="bg-[#0a0a0a] border border-[#2a2a2a] rounded-lg p-3">
<p class="text-white text-sm font-semibold text-center">📋 Termo Especial</p> <p class="text-white text-sm font-semibold text-center"><%= icone :termo_especial %> Termo Especial</p>
<p class="text-cyan-400 text-xs text-center mb-2"><%= moeda(@precos[:termo_especial]) %> cada</p> <p class="text-cyan-400 text-xs text-center mb-2"><%= moeda(@precos[:termo_especial]) %> cada</p>
<div class="flex items-center justify-center gap-2"> <div class="flex items-center justify-center gap-2">
<button type="button" data-action="apontamento-termo#menosEspecial" <button type="button" data-action="apontamento-termo#menosEspecial"
@@ -338,7 +341,7 @@
<button type="button" data-action="apontamento-termo#adicionar" <button type="button" data-action="apontamento-termo#adicionar"
class="w-full bg-green-600 hover:bg-green-500 text-white font-bold py-3.5 rounded-lg min-h-[48px]"> class="w-full bg-green-600 hover:bg-green-500 text-white font-bold py-3.5 rounded-lg min-h-[48px]">
Adicionar termos Adicionar termos <%= icone :confirmar, cor: nil %>
</button> </button>
</div> </div>
</div> </div>
@@ -351,10 +354,10 @@
<%# ── Lista de entregas (3 colunas) — área rolável ──── %> <%# ── Lista de entregas (3 colunas) — área rolável ──── %>
<div class="lg:col-span-3 space-y-2 lg:overflow-y-auto lg:pr-1" data-validacao-target="listaCol"> <div class="lg:col-span-3 space-y-2 lg:overflow-y-auto lg:pr-1" data-validacao-target="listaCol">
<%# 🔍 Busca na listagem — filtra os cards por NF, local, endereço ou veículo %> <%# Busca na listagem — filtra os cards por NF, local, endereço ou veículo %>
<div class="sticky top-0 z-20 bg-[#0a0a0a] pb-2"> <div class="sticky top-0 z-20 bg-[#0a0a0a] pb-2">
<div class="relative"> <div class="relative">
<span class="absolute left-3 top-1/2 -translate-y-1/2 text-gray-500 pointer-events-none">🔍</span> <span class="absolute left-3 top-1/2 -translate-y-1/2 pointer-events-none"><%= icone :buscar, cor: 'text-gray-500' %></span>
<input type="search" data-validacao-target="busca" data-action="input->validacao#filtrarBusca" <input type="search" data-validacao-target="busca" data-action="input->validacao#filtrarBusca"
placeholder="Buscar NF, local, endereço ou veículo…" autocomplete="off" placeholder="Buscar NF, local, endereço ou veículo…" autocomplete="off"
class="w-full bg-[#1a1a1a] border border-[#2a2a2a] rounded-lg pl-10 pr-3 py-2.5 text-sm text-gray-200 placeholder-gray-500 focus:border-orange-500 focus:outline-none"> class="w-full bg-[#1a1a1a] border border-[#2a2a2a] rounded-lg pl-10 pr-3 py-2.5 text-sm text-gray-200 placeholder-gray-500 focus:border-orange-500 focus:outline-none">
@@ -379,15 +382,15 @@
<p class="text-white font-semibold truncate"> <p class="text-white font-semibold truncate">
NF <%= e.numero_nf %> · <%= e.local %> NF <%= e.numero_nf %> · <%= e.local %>
<% if e.falhada? %> <% if e.falhada? %>
<span class="ml-1 align-middle text-[10px] font-bold uppercase tracking-wide bg-red-500/20 text-red-400 border border-red-500/40 px-1.5 py-0.5 rounded">⚠️ Insucesso</span> <span class="ml-1 align-middle text-[10px] font-bold uppercase tracking-wide bg-red-500/20 text-red-400 border border-red-500/40 px-1.5 py-0.5 rounded"><%= icone :alerta, cor: nil, tamanho: 'w-3 h-3' %> Insucesso</span>
<% else %> <% else %>
<span class="ml-1 align-middle text-[10px] font-bold uppercase tracking-wide bg-green-500/20 text-green-400 border border-green-500/40 px-1.5 py-0.5 rounded"> Entregue</span> <span class="ml-1 align-middle text-[10px] font-bold uppercase tracking-wide bg-green-500/20 text-green-400 border border-green-500/40 px-1.5 py-0.5 rounded"><%= icone :confirmar, cor: nil, tamanho: 'w-3 h-3' %> Entregue</span>
<% end %> <% end %>
</p> </p>
<p class="text-gray-400 text-xs truncate">📍 <%= e.address.presence || 'sem endereço' %></p> <p class="text-gray-400 text-xs truncate"><%= icone :local %> <%= e.address.presence || 'sem endereço' %></p>
<p class="text-gray-500 text-xs"> <p class="text-gray-500 text-xs">
📅 <%= e.checkout&.strftime('%d/%m/%Y') || '—' %> · 🚚 <%= e.vehicle.presence || 'sem veículo' %> <%= icone :calendario %> <%= e.checkout&.strftime('%d/%m/%Y') || '—' %> · <%= icone :caminhao %> <%= e.vehicle.presence || 'sem veículo' %>
<% if e.atraso_minutos > 0 %> · ⏱️ atraso <%= e.atraso_minutos %>min<% end %> <% if e.atraso_minutos > 0 %> · <%= icone :relogio, cor: 'text-amber-400' %> atraso <%= e.atraso_minutos %>min<% end %>
</p> </p>
</div> </div>
@@ -412,7 +415,7 @@
<% end %> <% end %>
<% else %> <% else %>
<div class="bg-[#1a1a1a] border border-[#2a2a2a] rounded-xl p-10 text-center"> <div class="bg-[#1a1a1a] border border-[#2a2a2a] rounded-xl p-10 text-center">
<p class="text-4xl mb-3">🎉</p> <p class="mb-3"><%= icone :sucesso, tamanho: 'w-10 h-10', cor: 'text-green-500' %></p>
<p class="text-gray-400">Nenhuma entrega pendente neste filtro.</p> <p class="text-gray-400">Nenhuma entrega pendente neste filtro.</p>
</div> </div>
<% end %> <% end %>
@@ -421,7 +424,7 @@
<%# ── Resumo lateral (1 coluna) — rola junto se precisar ─────── %> <%# ── Resumo lateral (1 coluna) — rola junto se precisar ─────── %>
<div class="lg:col-span-1 lg:overflow-y-auto" data-validacao-target="resumo"> <div class="lg:col-span-1 lg:overflow-y-auto" data-validacao-target="resumo">
<div class="bg-[#1a1a1a] border border-[#2a2a2a] rounded-xl p-5 space-y-4"> <div class="bg-[#1a1a1a] border border-[#2a2a2a] rounded-xl p-5 space-y-4">
<h3 class="text-white font-bold">📊 Resumo</h3> <h3 class="text-white font-bold flex items-center gap-2"><%= icone :dashboard %> Resumo</h3>
<div class="space-y-2 text-sm"> <div class="space-y-2 text-sm">
<div class="flex justify-between items-center"> <div class="flex justify-between items-center">
@@ -465,13 +468,13 @@
</div> </div>
<div class="border-t border-[#2a2a2a] pt-4 text-xs text-gray-500 space-y-1"> <div class="border-t border-[#2a2a2a] pt-4 text-xs text-gray-500 space-y-1">
<p>💰 Normal: <%= moeda(@precos[:entrega_normal]) %></p> <p><%= icone :dinheiro %> Normal: <%= moeda(@precos[:entrega_normal]) %></p>
<p>📦 Retirada: <%= moeda(@precos[:retirada]) %></p> <p><%= icone :retirada %> Retirada: <%= moeda(@precos[:retirada]) %></p>
<p> Bônus: <%= moeda(@precos[:bonus]) %></p> <p><%= icone :bonus %> Bônus: <%= moeda(@precos[:bonus]) %></p>
<p>⚠️ Desconto: -<%= moeda(@precos[:desconto]) %></p> <p><%= icone :desconto %> Desconto: -<%= moeda(@precos[:desconto]) %></p>
<p> Extraordinária: <%= moeda(@precos[:extraordinaria]) %> (valor por entrega)</p> <p><%= icone :extraordinaria %> Extraordinária: <%= moeda(@precos[:extraordinaria]) %> (valor por entrega)</p>
<p>📄 Termo: <%= moeda(@precos[:termo]) %></p> <p><%= icone :documento %> Termo: <%= moeda(@precos[:termo]) %></p>
<p>📋 Termo Especial: <%= moeda(@precos[:termo_especial]) %></p> <p><%= icone :termo_especial %> Termo Especial: <%= moeda(@precos[:termo_especial]) %></p>
</div> </div>
</div> </div>
</div> </div>

View File

@@ -7,7 +7,7 @@
</div> </div>
<div style="background: #ffffff; padding: 28px;"> <div style="background: #ffffff; padding: 28px;">
<h2 style="color: #0a0a0a; margin-top: 0;">Olá, <%= @user.nome.split.first %>!</h2> <h2 style="color: #0a0a0a; margin-top: 0;">Olá, <%= @user.nome.split.first %>!</h2>
<p style="color: #374151; font-size: 15px; line-height: 1.6;"> <p style="color: #374151; font-size: 15px; line-height: 1.6;">
Seu pagamento de entregas foi <strong>efetuado</strong> pela empresa. Seu pagamento de entregas foi <strong>efetuado</strong> pela empresa.

View File

@@ -7,7 +7,7 @@
</div> </div>
<div style="background: #ffffff; padding: 28px;"> <div style="background: #ffffff; padding: 28px;">
<h2 style="color: #0a0a0a; margin-top: 0;">Olá, <%= @user.nome.split.first %>! 👋</h2> <h2 style="color: #0a0a0a; margin-top: 0;">Olá, <%= @user.nome.split.first %>!</h2>
<p style="color: #374151; font-size: 15px; line-height: 1.6;"> <p style="color: #374151; font-size: 15px; line-height: 1.6;">
Seu pagamento de entregas foi fechado pela empresa. Seu pagamento de entregas foi fechado pela empresa.

View File

@@ -2,8 +2,8 @@
<% content_for :title, 'Consolidações arquivadas' %> <% content_for :title, 'Consolidações arquivadas' %>
<div class="flex items-center justify-between mb-6"> <div class="flex items-center justify-between mb-6">
<h1 class="text-2xl font-bold text-white">📁 Consolidações arquivadas</h1> <h1 class="text-2xl font-bold text-white flex items-center gap-2"><%= icone :arquivadas %> Consolidações arquivadas</h1>
<%= link_to 'Voltar às ativas', consolidacoes_path, <%= link_to rotulo(:voltar, 'Voltar às ativas', cor: nil), consolidacoes_path,
class: 'text-gray-400 hover:text-white border border-[#2a2a2a] px-4 py-3 rounded-lg min-h-[48px] flex items-center' %> class: 'text-gray-400 hover:text-white border border-[#2a2a2a] px-4 py-3 rounded-lg min-h-[48px] flex items-center' %>
</div> </div>
@@ -26,12 +26,12 @@
<%= badge_status(c.status) %> <%= badge_status(c.status) %>
</div> </div>
<p class="text-gray-400 text-sm"> <p class="text-gray-400 text-sm">
📅 <%= l c.data_inicio, format: :short %> <%= l c.data_fim, format: :short %> <%= icone :calendario %> <%= l c.data_inicio, format: :short %> <%= icone :avancar, tamanho: 'w-3 h-3' %> <%= l c.data_fim, format: :short %>
· 👤 <%= c.consolidacao_motoristas.count %> motorista(s) · <%= icone :pessoa %> <%= c.consolidacao_motoristas.count %> motorista(s)
· Criada por <%= c.criador.nome_display %> · Criada por <%= c.criador.nome_display %>
</p> </p>
<p class="text-gray-500 text-xs mt-1"> <p class="text-gray-500 text-xs mt-1">
📁 Arquivada <%= icone :arquivar %> Arquivada
<% if c.arquivado_em.present? %>em <%= l c.arquivado_em, format: :short %><% end %> <% if c.arquivado_em.present? %>em <%= l c.arquivado_em, format: :short %><% end %>
<% if c.arquivador.present? %>por <%= c.arquivador.nome_display %><% end %> <% if c.arquivador.present? %>por <%= c.arquivador.nome_display %><% end %>
</p> </p>
@@ -45,12 +45,16 @@
<% if policy(c).destroy? %> <% if policy(c).destroy? %>
<div class="flex gap-2"> <div class="flex gap-2">
<%= button_to '♻️ Reativar', reativar_consolidacao_path(c), method: :post, <%= button_to reativar_consolidacao_path(c), method: :post,
form: { data: { turbo_confirm: 'Reativar esta consolidação? Ela voltará para a lista de ativas.' } }, form: { data: { turbo_confirm: 'Reativar esta consolidação? Ela voltará para a lista de ativas.' } },
class: 'bg-orange-500 hover:bg-orange-600 text-black font-bold px-4 py-2.5 rounded-lg text-sm min-h-[44px]' %> class: 'bg-orange-500 hover:bg-orange-600 text-black font-bold px-4 py-2.5 rounded-lg text-sm min-h-[44px] inline-flex items-center gap-2' do %>
<%= button_to '🗑️ Excluir', consolidacao_path(c), method: :delete, <%= icone :restaurar, cor: nil, tamanho: 'w-4 h-4' %> Reativar
<% end %>
<%= button_to consolidacao_path(c), method: :delete,
form: { data: { turbo_confirm: 'EXCLUIR PERMANENTEMENTE esta consolidação e todas as suas classificações? Esta ação NÃO pode ser desfeita.' } }, form: { data: { turbo_confirm: 'EXCLUIR PERMANENTEMENTE esta consolidação e todas as suas classificações? Esta ação NÃO pode ser desfeita.' } },
class: 'text-red-400 hover:text-white hover:bg-red-600 border border-red-900 px-4 py-2.5 rounded-lg text-sm min-h-[44px]' %> class: 'text-red-400 hover:text-white hover:bg-red-600 border border-red-900 px-4 py-2.5 rounded-lg text-sm min-h-[44px] inline-flex items-center gap-2' do %>
<%= icone :excluir, cor: nil, tamanho: 'w-4 h-4' %> Excluir
<% end %>
</div> </div>
<% end %> <% end %>
</div> </div>
@@ -60,7 +64,7 @@
</div> </div>
<% else %> <% else %>
<div class="bg-[#1a1a1a] border border-[#2a2a2a] rounded-xl p-12 text-center"> <div class="bg-[#1a1a1a] border border-[#2a2a2a] rounded-xl p-12 text-center">
<p class="text-5xl mb-4">🗂️</p> <p class="mb-4"><%= icone :pasta, tamanho: 'w-12 h-12', cor: 'text-gray-600' %></p>
<p class="text-gray-400 mb-4">Nenhuma consolidação arquivada.</p> <p class="text-gray-400 mb-4">Nenhuma consolidação arquivada.</p>
<%= link_to 'Ver consolidações ativas', consolidacoes_path, <%= link_to 'Ver consolidações ativas', consolidacoes_path,
class: 'inline-block bg-orange-500 hover:bg-orange-600 text-black font-bold px-5 py-3 rounded-lg' %> class: 'inline-block bg-orange-500 hover:bg-orange-600 text-black font-bold px-5 py-3 rounded-lg' %>

View File

@@ -4,14 +4,14 @@
<div data-controller="nota-avulsa" <div data-controller="nota-avulsa"
data-nota-avulsa-buscar-url-value="<%= buscar_nf_consolidacoes_path %>"> data-nota-avulsa-buscar-url-value="<%= buscar_nf_consolidacoes_path %>">
<div class="flex items-center justify-between mb-6"> <div class="flex items-center justify-between mb-6">
<h1 class="text-2xl font-bold text-white">📦 Consolidações</h1> <h1 class="text-2xl font-bold text-white flex items-center gap-2"><%= icone :consolidacoes %> Consolidações</h1>
<div class="flex gap-2"> <div class="flex gap-2">
<%= link_to '📁 Arquivadas', arquivadas_consolidacoes_path, <%= link_to rotulo(:arquivadas, 'Arquivadas'), arquivadas_consolidacoes_path,
class: 'text-gray-400 hover:text-white border border-[#2a2a2a] px-4 py-3 rounded-lg min-h-[48px] flex items-center transition-colors' %> class: 'text-gray-400 hover:text-white border border-[#2a2a2a] px-4 py-3 rounded-lg min-h-[48px] flex items-center transition-colors' %>
<% if policy(Consolidacao).create? %> <% if policy(Consolidacao).create? %>
<button type="button" data-action="nota-avulsa#abrir" <button type="button" data-action="nota-avulsa#abrir"
class="text-white border border-dashed border-orange-500/60 hover:border-orange-500 font-semibold px-4 py-3 rounded-lg min-h-[48px] flex items-center transition-colors"> class="text-white border border-dashed border-orange-500/60 hover:border-orange-500 font-semibold px-4 py-3 rounded-lg min-h-[48px] flex items-center gap-2 transition-colors">
Nota avulsa <%= icone :adicionar %> Nota avulsa
</button> </button>
<% end %> <% end %>
<%= link_to '+ Nova Consolidação', new_consolidacao_path, <%= link_to '+ Nova Consolidação', new_consolidacao_path,
@@ -25,9 +25,9 @@
class="hidden fixed inset-0 z-50 bg-black/70 flex items-center justify-center p-4"> class="hidden fixed inset-0 z-50 bg-black/70 flex items-center justify-center p-4">
<div class="bg-[#1a1a1a] border border-[#2a2a2a] rounded-2xl w-full max-w-lg p-6 max-h-[90vh] overflow-y-auto text-left"> <div class="bg-[#1a1a1a] border border-[#2a2a2a] rounded-2xl w-full max-w-lg p-6 max-h-[90vh] overflow-y-auto text-left">
<div class="flex items-center justify-between mb-1"> <div class="flex items-center justify-between mb-1">
<h2 class="text-xl font-bold text-white"> Nota avulsa</h2> <h2 class="text-xl font-bold text-white flex items-center gap-2"><%= icone :adicionar %> Nota avulsa</h2>
<button type="button" data-action="nota-avulsa#fechar" <button type="button" data-action="nota-avulsa#fechar"
class="text-gray-500 hover:text-white text-2xl leading-none"></button> class="text-gray-500 hover:text-white leading-none" aria-label="Fechar"><%= icone :fechar, cor: nil, tamanho: 'w-5 h-5' %></button>
</div> </div>
<p class="text-gray-400 text-sm mb-4">Para uma nota que chegou por fora: busque a NF e crie uma consolidação já com o apontamento do motorista.</p> <p class="text-gray-400 text-sm mb-4">Para uma nota que chegou por fora: busque a NF e crie uma consolidação já com o apontamento do motorista.</p>
@@ -50,14 +50,14 @@
<div data-nota-avulsa-target="confirmar" class="hidden border-t border-[#2a2a2a] pt-4"> <div data-nota-avulsa-target="confirmar" class="hidden border-t border-[#2a2a2a] pt-4">
<label class="block text-gray-400 text-sm mb-2">Classificar como <span class="text-gray-600">(pode marcar mais de uma)</span></label> <label class="block text-gray-400 text-sm mb-2">Classificar como <span class="text-gray-600">(pode marcar mais de uma)</span></label>
<div class="grid grid-cols-2 gap-2 mb-4"> <div class="grid grid-cols-2 gap-2 mb-4">
<% [['entrega_normal', '🚚 Entrega Normal', Configuracao.preco_entrega], <% [['entrega_normal', :caminhao, 'Entrega Normal', Configuracao.preco_entrega],
['retirada', '📦 Retirada', Configuracao.preco_retirada], ['retirada', :retirada, 'Retirada', Configuracao.preco_retirada],
['bonus', 'Bônus', Configuracao.preco_bonus], ['bonus', :bonus, 'Bônus', Configuracao.preco_bonus],
['desconto', '⚠️ Desconto', Configuracao.preco_desconto], ['desconto', :desconto, 'Desconto', Configuracao.preco_desconto],
['extraordinaria', 'Entrega Extraordinária', Configuracao.preco_extraordinaria]].each do |tipo, label, preco| %> ['extraordinaria', :extraordinaria, 'Entrega Extraordinária', Configuracao.preco_extraordinaria]].each do |tipo, ic, label, preco| %>
<label class="flex items-center gap-2 bg-[#0a0a0a] border border-[#2a2a2a] rounded-lg px-3 py-3 cursor-pointer hover:border-orange-500 text-sm text-white"> <label class="flex items-center gap-2 bg-[#0a0a0a] border border-[#2a2a2a] rounded-lg px-3 py-3 cursor-pointer hover:border-orange-500 text-sm text-white">
<%= check_box_tag 'tipos[]', tipo, tipo == 'entrega_normal', id: nil, class: 'accent-orange-500' %> <%= check_box_tag 'tipos[]', tipo, tipo == 'entrega_normal', id: nil, class: 'accent-orange-500' %>
<span><%= label %><br><span class="text-gray-500 text-xs"><%= moeda(preco) %></span></span> <span><%= icone ic %> <%= label %><br><span class="text-gray-500 text-xs"><%= moeda(preco) %></span></span>
</label> </label>
<% end %> <% end %>
</div> </div>
@@ -77,11 +77,11 @@
class: 'bg-[#0a0a0a] border border-[#2a2a2a] text-white rounded-lg px-3 py-2.5 focus:border-orange-500 focus:outline-none' %> class: 'bg-[#0a0a0a] border border-[#2a2a2a] text-white rounded-lg px-3 py-2.5 focus:border-orange-500 focus:outline-none' %>
<%= f.select :status, <%= f.select :status,
options_for_select([['Todos os status', ''], ['📝 Rascunho', 'rascunho'], ['Finalizada', 'finalizada']], params[:status]), options_for_select([['Todos os status', ''], ['Rascunho', 'rascunho'], ['Finalizada', 'finalizada']], params[:status]),
{}, class: 'bg-[#0a0a0a] border border-[#2a2a2a] text-white rounded-lg px-3 py-2.5' %> {}, class: 'bg-[#0a0a0a] border border-[#2a2a2a] text-white rounded-lg px-3 py-2.5' %>
<%= f.select :pagamento, <%= f.select :pagamento,
options_for_select([['💵 Pagamento (todos)', ''], ['💰 Pagas', 'pago'], ['Parciais', 'parcial'], ['🕗 Pendentes', 'pendente']], params[:pagamento]), options_for_select([['Pagamento (todos)', ''], ['Pagas', 'pago'], ['Parciais', 'parcial'], ['Pendentes', 'pendente']], params[:pagamento]),
{}, class: 'bg-[#0a0a0a] border border-[#2a2a2a] text-white rounded-lg px-3 py-2.5' %> {}, class: 'bg-[#0a0a0a] border border-[#2a2a2a] text-white rounded-lg px-3 py-2.5' %>
<%= f.date_field :inicio, value: params[:inicio], <%= f.date_field :inicio, value: params[:inicio],
@@ -110,12 +110,12 @@
<% if c.finalizada? %><%= badge_pagamento(c.status_pagamento) %><% end %> <% if c.finalizada? %><%= badge_pagamento(c.status_pagamento) %><% end %>
</div> </div>
<p class="text-gray-400 text-sm"> <p class="text-gray-400 text-sm">
📅 <%= l c.data_inicio, format: :short %> → <%= l c.data_fim, format: :short %> <%= icone :calendario %> <%= l c.data_inicio, format: :short %> → <%= l c.data_fim, format: :short %>
· 👤 <%= c.consolidacao_motoristas.count %> motorista(s) · <%= icone :pessoa %> <%= c.consolidacao_motoristas.count %> motorista(s)
· Criada por <%= c.criador.nome_display %> · Criada por <%= c.criador.nome_display %>
</p> </p>
<% if c.operacoes.present? %> <% if c.operacoes.present? %>
<p class="text-gray-500 text-xs mt-1">🗂️ <%= c.operacoes_label.join(' · ') %></p> <p class="text-gray-500 text-xs mt-1"><%= icone :pasta %> <%= c.operacoes_label.join(' · ') %></p>
<% end %> <% end %>
</div> </div>
<div class="text-right"> <div class="text-right">
@@ -128,7 +128,7 @@
</div> </div>
<% else %> <% else %>
<div class="bg-[#1a1a1a] border border-[#2a2a2a] rounded-xl p-12 text-center"> <div class="bg-[#1a1a1a] border border-[#2a2a2a] rounded-xl p-12 text-center">
<p class="text-5xl mb-4">📭</p> <p class="mb-4"><%= icone :vazio, tamanho: 'w-12 h-12', cor: 'text-gray-600' %></p>
<p class="text-gray-400 mb-4">Nenhuma consolidação encontrada.</p> <p class="text-gray-400 mb-4">Nenhuma consolidação encontrada.</p>
<%= link_to 'Criar primeira consolidação', new_consolidacao_path, <%= link_to 'Criar primeira consolidação', new_consolidacao_path,
class: 'inline-block bg-orange-500 hover:bg-orange-600 text-black font-bold px-5 py-3 rounded-lg' %> class: 'inline-block bg-orange-500 hover:bg-orange-600 text-black font-bold px-5 py-3 rounded-lg' %>

View File

@@ -2,13 +2,13 @@
<% content_for :title, 'Nova Consolidação' %> <% content_for :title, 'Nova Consolidação' %>
<div class="max-w-3xl mx-auto"> <div class="max-w-3xl mx-auto">
<h1 class="text-2xl font-bold text-white mb-6"> Nova Consolidação</h1> <h1 class="text-2xl font-bold text-white mb-6 flex items-center gap-2"><%= icone :adicionar %> Nova Consolidação</h1>
<%= form_with model: @consolidacao, class: 'bg-[#1a1a1a] border border-[#2a2a2a] rounded-xl p-6 space-y-6' do |f| %> <%= form_with model: @consolidacao, class: 'bg-[#1a1a1a] border border-[#2a2a2a] rounded-xl p-6 space-y-6' do |f| %>
<% if @consolidacao.errors.any? %> <% if @consolidacao.errors.any? %>
<div class="bg-red-900/40 border border-red-600 rounded-lg p-4"> <div class="bg-red-900/40 border border-red-600 rounded-lg p-4">
<p class="text-red-400 font-bold mb-1">⚠️ Corrija os erros:</p> <p class="text-red-400 font-bold mb-1 flex items-center gap-2"><%= icone :alerta, cor: nil %> Corrija os erros:</p>
<ul class="text-red-300 text-sm list-disc list-inside"> <ul class="text-red-300 text-sm list-disc list-inside">
<% @consolidacao.errors.full_messages.each do |msg| %> <% @consolidacao.errors.full_messages.each do |msg| %>
<li><%= msg %></li> <li><%= msg %></li>
@@ -20,7 +20,7 @@
<%# Operação — multi-select opcional (pré-preenche e filtra por NF) %> <%# Operação — multi-select opcional (pré-preenche e filtra por NF) %>
<div> <div>
<label class="block text-white font-semibold mb-2"> <label class="block text-white font-semibold mb-2">
🗂️ Operação <span class="text-gray-500 font-normal text-sm">(opcional — preenche motoristas, veículos e período, e filtra as entregas por nota fiscal)</span> <%= icone :pasta %> Operação <span class="text-gray-500 font-normal text-sm">(opcional — preenche motoristas, veículos e período, e filtra as entregas por nota fiscal)</span>
</label> </label>
<div id="operacoes-lista" <div id="operacoes-lista"
class="bg-[#0a0a0a] border border-[#2a2a2a] rounded-lg p-3 max-h-48 overflow-y-auto space-y-1"> class="bg-[#0a0a0a] border border-[#2a2a2a] rounded-lg p-3 max-h-48 overflow-y-auto space-y-1">

View File

@@ -15,7 +15,7 @@
<% if @consolidacao.finalizada? %><%= badge_pagamento(@consolidacao.status_pagamento) %><% end %> <% if @consolidacao.finalizada? %><%= badge_pagamento(@consolidacao.status_pagamento) %><% end %>
</div> </div>
<p class="text-gray-400 text-sm mt-1"> <p class="text-gray-400 text-sm mt-1">
📅 <%= l @consolidacao.data_inicio, format: :short %> <%= l @consolidacao.data_fim, format: :short %> <%= icone :calendario %> <%= l @consolidacao.data_inicio, format: :short %> <%= icone :avancar, tamanho: 'w-3 h-3' %> <%= l @consolidacao.data_fim, format: :short %>
· Criada por <%= @consolidacao.criador.nome_display %> · Criada por <%= @consolidacao.criador.nome_display %>
<% if @consolidacao.finalizada? %> <% if @consolidacao.finalizada? %>
· Finalizada por <%= @consolidacao.finalizador&.nome_display %> em <%= l @consolidacao.finalizado_em, format: :short %> · Finalizada por <%= @consolidacao.finalizador&.nome_display %> em <%= l @consolidacao.finalizado_em, format: :short %>
@@ -23,7 +23,7 @@
</p> </p>
<% if @consolidacao.operacoes.present? %> <% if @consolidacao.operacoes.present? %>
<p class="text-gray-400 text-sm mt-1"> <p class="text-gray-400 text-sm mt-1">
🗂️ Operação: <%= icone :pasta %> Operação:
<% @consolidacao.operacoes_label.each do |op| %> <% @consolidacao.operacoes_label.each do |op| %>
<span class="inline-block bg-[#2a2a2a] text-gray-200 rounded-full px-2.5 py-0.5 text-xs"><%= op %></span> <span class="inline-block bg-[#2a2a2a] text-gray-200 rounded-full px-2.5 py-0.5 text-xs"><%= op %></span>
<% end %> <% end %>
@@ -32,16 +32,18 @@
</div> </div>
<div class="flex gap-2"> <div class="flex gap-2">
<% if @consolidacao.rascunho? %> <% if @consolidacao.rascunho? %>
<%= link_to '✏️ Continuar validação', wizard_consolidacao_path(@consolidacao), <%= link_to rotulo(:editar, 'Continuar validação', cor: nil), wizard_consolidacao_path(@consolidacao),
class: 'bg-orange-500 hover:bg-orange-600 text-black font-bold px-5 py-3 rounded-lg min-h-[48px] flex items-center' %> class: 'bg-orange-500 hover:bg-orange-600 text-black font-bold px-5 py-3 rounded-lg min-h-[48px] flex items-center' %>
<% end %> <% end %>
<%= link_to '📊 Relatório financeiro', gerar_pdf_financeiro_consolidacao_path(@consolidacao), <%= link_to rotulo(:dashboard, 'Relatório financeiro'), gerar_pdf_financeiro_consolidacao_path(@consolidacao),
data: { turbo: false }, data: { turbo: false },
class: 'bg-[#1a1a1a] hover:bg-[#2a2a2a] text-white border border-orange-500 font-semibold px-4 py-3 rounded-lg min-h-[48px] flex items-center' %> class: 'bg-[#1a1a1a] hover:bg-[#2a2a2a] text-white border border-orange-500 font-semibold px-4 py-3 rounded-lg min-h-[48px] flex items-center' %>
<% if policy(@consolidacao).destroy? && !@consolidacao.arquivada? %> <% if policy(@consolidacao).destroy? && !@consolidacao.arquivada? %>
<%= button_to '📁 Arquivar', arquivar_consolidacao_path(@consolidacao), method: :post, <%= button_to arquivar_consolidacao_path(@consolidacao), method: :post,
form: { data: { turbo_confirm: 'Arquivar esta consolidação? Ela não será excluída, apenas movida para o arquivo.' } }, form: { data: { turbo_confirm: 'Arquivar esta consolidação? Ela não será excluída, apenas movida para o arquivo.' } },
class: 'text-gray-400 hover:text-white border border-[#2a2a2a] px-4 py-3 rounded-lg min-h-[48px]' %> class: 'text-gray-400 hover:text-white border border-[#2a2a2a] px-4 py-3 rounded-lg min-h-[48px] flex items-center gap-2' do %>
<%= icone :arquivar, cor: nil, tamanho: 'w-4 h-4' %> Arquivar
<% end %>
<% end %> <% end %>
</div> </div>
</div> </div>
@@ -49,7 +51,7 @@
<%# ── Pagamento da operação inteira ─────────────────────── %> <%# ── Pagamento da operação inteira ─────────────────────── %>
<% if pode_pagar %> <% if pode_pagar %>
<div class="flex flex-col sm:flex-row sm:items-center gap-2 mt-4 bg-[#1a1a1a] border border-[#2a2a2a] rounded-xl p-4"> <div class="flex flex-col sm:flex-row sm:items-center gap-2 mt-4 bg-[#1a1a1a] border border-[#2a2a2a] rounded-xl p-4">
<span class="text-gray-400 text-sm font-medium mr-1">💵 Operação inteira:</span> <span class="text-gray-400 text-sm font-medium mr-1"><%= icone :pagamento %> Operação inteira:</span>
<% if @consolidacao.status_pagamento != :pago %> <% if @consolidacao.status_pagamento != :pago %>
<%# Form único (id) — os inputs de NF e os botões "Marcar pago" de cada <%# Form único (id) — os inputs de NF e os botões "Marcar pago" de cada
linha se associam a ele via atributo HTML5 form=, evitando <form> aninhado. %> linha se associam a ele via atributo HTML5 form=, evitando <form> aninhado. %>
@@ -57,15 +59,19 @@
id: 'form-pagar-tudo', class: 'flex items-center gap-2' do |f| %> id: 'form-pagar-tudo', class: 'flex items-center gap-2' do |f| %>
<%= f.select :forma_pagamento, options_for_select(formas_pagamento, 'pix'), <%= f.select :forma_pagamento, options_for_select(formas_pagamento, 'pix'),
{}, class: 'bg-[#0a0a0a] border border-[#2a2a2a] text-white rounded-lg px-3 py-2.5 text-sm' %> {}, class: 'bg-[#0a0a0a] border border-[#2a2a2a] text-white rounded-lg px-3 py-2.5 text-sm' %>
<%= f.submit '💰 Marcar tudo como pago', <%= f.button type: :submit,
data: { turbo_confirm: 'Marcar TODOS os motoristas pendentes desta consolidação como PAGOS? Confirme que a Nota Fiscal de cada um foi preenchida.' }, data: { turbo_confirm: 'Marcar TODOS os motoristas pendentes desta consolidação como PAGOS? Confirme que a Nota Fiscal de cada um foi preenchida.' },
class: 'bg-green-600 hover:bg-green-700 text-white font-bold px-4 py-2.5 rounded-lg text-sm cursor-pointer min-h-[44px]' %> class: 'bg-green-600 hover:bg-green-700 text-white font-bold px-4 py-2.5 rounded-lg text-sm cursor-pointer min-h-[44px] inline-flex items-center gap-2' do %>
<%= icone :dinheiro, cor: nil, tamanho: 'w-4 h-4' %> Marcar tudo como pago
<% end %>
<% end %> <% end %>
<% end %> <% end %>
<% if @consolidacao.status_pagamento != :pendente %> <% if @consolidacao.status_pagamento != :pendente %>
<%= button_to '↩ Estornar tudo', cancelar_pagamento_consolidacao_path(@consolidacao), method: :post, <%= button_to cancelar_pagamento_consolidacao_path(@consolidacao), method: :post,
data: { turbo_confirm: 'Estornar o pagamento de TODOS os motoristas?' }, form: { data: { turbo_confirm: 'Estornar o pagamento de TODOS os motoristas?' } },
class: 'text-gray-400 hover:text-white border border-[#2a2a2a] px-4 py-2.5 rounded-lg text-sm min-h-[44px]' %> class: 'text-gray-400 hover:text-white border border-[#2a2a2a] px-4 py-2.5 rounded-lg text-sm min-h-[44px] inline-flex items-center gap-2' do %>
<%= icone :estornar, cor: nil, tamanho: 'w-4 h-4' %> Estornar tudo
<% end %>
<% end %> <% end %>
</div> </div>
<% end %> <% end %>
@@ -78,7 +84,7 @@
</div> </div>
<%# Motoristas + botões de PDF %> <%# Motoristas + botões de PDF %>
<h2 class="text-white font-bold text-lg mb-3">👥 Motoristas</h2> <h2 class="text-white font-bold text-lg mb-3 flex items-center gap-2"><%= icone :usuarios %> Motoristas</h2>
<div class="space-y-3"> <div class="space-y-3">
<% @motoristas.each do |cm| %> <% @motoristas.each do |cm| %>
<div class="bg-[#1a1a1a] border border-[#2a2a2a] rounded-xl p-5"> <div class="bg-[#1a1a1a] border border-[#2a2a2a] rounded-xl p-5">
@@ -91,45 +97,49 @@
<% end %> <% end %>
</div> </div>
<p class="text-orange-500 font-black text-xl"><%= moeda(cm.valor_total) %></p> <p class="text-orange-500 font-black text-xl"><%= moeda(cm.valor_total) %></p>
<%# Breakdown por veículo (consolidados 🔒 / abertos). Informativo — o <%# Breakdown por veículo (consolidados / abertos). Informativo — o
pagamento continua sendo do motorista inteiro. %> pagamento continua sendo do motorista inteiro. %>
<% veiculos = @veiculos_por_motorista[cm.motorista_nome] %> <% veiculos = @veiculos_por_motorista[cm.motorista_nome] %>
<% if veiculos.present? %> <% if veiculos.present? %>
<div class="flex flex-wrap gap-1.5 mt-1"> <div class="flex flex-wrap gap-1.5 mt-1">
<% veiculos.each do |cv| %> <% veiculos.each do |cv| %>
<span class="text-xs px-2 py-0.5 rounded border <%= cv.fechado? ? 'border-green-500/50 text-green-400' : 'border-[#2a2a2a] text-gray-400' %>"> <span class="text-xs px-2 py-0.5 rounded border <%= cv.fechado? ? 'border-green-500/50 text-green-400' : 'border-[#2a2a2a] text-gray-400' %>">
<%= cv.fechado? ? '🔒' : '○' %> <%= cv.vehicle %> · <%= moeda(cv.valor_total) %> <%= cv.fechado? ? icone(:travado, cor: nil, tamanho: 'w-3 h-3') : icone(:ponto_vazio, cor: nil, tamanho: 'w-3 h-3') %> <%= cv.vehicle %> · <%= moeda(cv.valor_total) %>
</span> </span>
<% end %> <% end %>
</div> </div>
<% end %> <% end %>
<% if cm.pago? %> <% if cm.pago? %>
<p class="text-green-400 text-xs mt-1"> <p class="text-green-400 text-xs mt-1">
💰 Pago em <%= l cm.pago_em, format: :short %> <%= icone :dinheiro, cor: nil %> Pago em <%= l cm.pago_em, format: :short %>
<% if cm.forma_pagamento.present? %>via <%= cm.forma_pagamento.humanize %><% end %> <% if cm.forma_pagamento.present? %>via <%= cm.forma_pagamento.humanize %><% end %>
<% if cm.pagador.present? %>por <%= cm.pagador.nome_display %><% end %> <% if cm.pagador.present? %>por <%= cm.pagador.nome_display %><% end %>
</p> </p>
<% if cm.nota_fiscal.present? %> <% if cm.nota_fiscal.present? %>
<p class="text-gray-300 text-xs mt-0.5">🧾 NF <%= cm.nota_fiscal %></p> <p class="text-gray-300 text-xs mt-0.5"><%= icone :nota_fiscal %> NF <%= cm.nota_fiscal %></p>
<% end %> <% end %>
<% end %> <% end %>
<% if pode_pagar %> <% if pode_pagar %>
<div class="mt-2"> <div class="mt-2">
<% if cm.pago? %> <% if cm.pago? %>
<%= button_to '↩ Estornar pagamento', cancelar_pagamento_consolidacao_path(@consolidacao, consolidacao_motorista_id: cm.id), method: :post, <%= button_to cancelar_pagamento_consolidacao_path(@consolidacao, consolidacao_motorista_id: cm.id), method: :post,
data: { turbo_confirm: "Estornar o pagamento de #{cm.motorista_nome}?" }, form: { data: { turbo_confirm: "Estornar o pagamento de #{cm.motorista_nome}?" } },
class: 'text-gray-400 hover:text-white border border-[#2a2a2a] px-3 py-2 rounded-lg text-xs min-h-[40px]' %> class: 'text-gray-400 hover:text-white border border-[#2a2a2a] px-3 py-2 rounded-lg text-xs min-h-[40px] inline-flex items-center gap-1.5' do %>
<%= icone :estornar, cor: nil, tamanho: 'w-3.5 h-3.5' %> Estornar pagamento
<% end %>
<% else %> <% else %>
<%# Inputs/botão associados ao form único #form-pagar-tudo via atributo form=. %> <%# Inputs/botão associados ao form único #form-pagar-tudo via atributo form=. %>
<div class="flex items-center gap-2 flex-wrap"> <div class="flex items-center gap-2 flex-wrap">
<%= text_field_tag "notas_fiscais[#{cm.id}]", nil, form: 'form-pagar-tudo', <%= text_field_tag "notas_fiscais[#{cm.id}]", nil, form: 'form-pagar-tudo',
placeholder: 'Nº Nota Fiscal', placeholder: 'Nº Nota Fiscal',
class: 'bg-[#0a0a0a] border border-[#2a2a2a] text-white rounded-lg px-2 py-2 text-xs w-36' %> class: 'bg-[#0a0a0a] border border-[#2a2a2a] text-white rounded-lg px-2 py-2 text-xs w-36' %>
<%= button_tag '💰 Marcar pago', type: :submit, form: 'form-pagar-tudo', <%= button_tag type: :submit, form: 'form-pagar-tudo',
name: 'consolidacao_motorista_id', value: cm.id, name: 'consolidacao_motorista_id', value: cm.id,
data: { turbo_confirm: "Marcar #{cm.motorista_nome} como pago?" }, data: { turbo_confirm: "Marcar #{cm.motorista_nome} como pago?" },
class: 'bg-green-600 hover:bg-green-700 text-white font-bold px-3 py-2 rounded-lg text-xs cursor-pointer min-h-[40px]' %> class: 'bg-green-600 hover:bg-green-700 text-white font-bold px-3 py-2 rounded-lg text-xs cursor-pointer min-h-[40px] inline-flex items-center gap-1.5' do %>
<%= icone :dinheiro, cor: nil, tamanho: 'w-3.5 h-3.5' %> Marcar pago
<% end %>
</div> </div>
<% end %> <% end %>
</div> </div>
@@ -139,13 +149,13 @@
<%# Preview abre modal %> <%# Preview abre modal %>
<button onclick="abrirPreview('<%= preview_extrato_consolidacao_path(@consolidacao, motorista: cm.motorista_nome) %>')" <button onclick="abrirPreview('<%= preview_extrato_consolidacao_path(@consolidacao, motorista: cm.motorista_nome) %>')"
class="bg-[#0a0a0a] hover:bg-[#2a2a2a] text-white border border-orange-500 font-semibold px-4 py-3 rounded-lg text-sm min-h-[48px]"> class="bg-[#0a0a0a] hover:bg-[#2a2a2a] text-white border border-orange-500 font-semibold px-4 py-3 rounded-lg text-sm min-h-[48px]">
👁️ Ver antes de gerar <%= icone :visualizar %> Ver antes de gerar
</button> </button>
<%= link_to '📄 Relatório Individual', <%= link_to rotulo(:documento, 'Relatório Individual', cor: nil),
gerar_pdf_relatorio_consolidacao_path(@consolidacao, motorista: cm.motorista_nome), gerar_pdf_relatorio_consolidacao_path(@consolidacao, motorista: cm.motorista_nome),
data: { turbo: false }, data: { turbo: false },
class: 'bg-orange-800 hover:bg-orange-700 text-white font-semibold px-4 py-3 rounded-lg text-sm min-h-[48px] flex items-center' %> class: 'bg-orange-800 hover:bg-orange-700 text-white font-semibold px-4 py-3 rounded-lg text-sm min-h-[48px] flex items-center' %>
<%= link_to '🧾 Gerar Extrato', <%= link_to rotulo(:nota_fiscal, 'Gerar Extrato', cor: nil),
gerar_pdf_extrato_consolidacao_path(@consolidacao, motorista: cm.motorista_nome), gerar_pdf_extrato_consolidacao_path(@consolidacao, motorista: cm.motorista_nome),
data: { turbo: false }, data: { turbo: false },
class: 'bg-orange-500 hover:bg-orange-600 text-black font-bold px-4 py-3 rounded-lg text-sm min-h-[48px] flex items-center' %> class: 'bg-orange-500 hover:bg-orange-600 text-black font-bold px-4 py-3 rounded-lg text-sm min-h-[48px] flex items-center' %>
@@ -158,7 +168,7 @@
<%# ── Apontamentos manuais (notas que chegaram por fora) ─────────────── %> <%# ── Apontamentos manuais (notas que chegaram por fora) ─────────────── %>
<% if @apontamentos.present? %> <% if @apontamentos.present? %>
<% pode_editar = !(@consolidacao.finalizada? && current_user.operador?) %> <% pode_editar = !(@consolidacao.finalizada? && current_user.operador?) %>
<h2 class="text-white font-bold text-lg mt-8 mb-3"> Apontamentos manuais</h2> <h2 class="text-white font-bold text-lg mt-8 mb-3 flex items-center gap-2"><%= icone :adicionar %> Apontamentos manuais</h2>
<div class="bg-[#1a1a1a] border border-[#2a2a2a] rounded-xl overflow-hidden"> <div class="bg-[#1a1a1a] border border-[#2a2a2a] rounded-xl overflow-hidden">
<table class="w-full text-sm"> <table class="w-full text-sm">
<thead class="bg-[#0a0a0a] text-gray-400"> <thead class="bg-[#0a0a0a] text-gray-400">
@@ -179,9 +189,9 @@
<tr> <tr>
<td class="px-4 py-3"> <td class="px-4 py-3">
<p class="text-white font-bold"><%= if pilares.first.termo? <p class="text-white font-bold"><%= if pilares.first.termo?
'📄 Entrega de termo' rotulo(:documento, 'Entrega de termo')
elsif pilares.first.termo_especial? elsif pilares.first.termo_especial?
'📋 Entrega de termo especial' rotulo(:termo_especial, 'Entrega de termo especial')
else else
"NF #{nf_exibida.presence || '—'}" "NF #{nf_exibida.presence || '—'}"
end %></p> end %></p>
@@ -200,11 +210,12 @@
<td class="px-4 py-3 text-right font-bold <%= total.negative? ? 'text-red-400' : 'text-white' %>"><%= moeda(total) %></td> <td class="px-4 py-3 text-right font-bold <%= total.negative? ? 'text-red-400' : 'text-white' %>"><%= moeda(total) %></td>
<% if pode_editar %> <% if pode_editar %>
<td class="px-4 py-3 text-right"> <td class="px-4 py-3 text-right">
<%= button_to '✕ Remover', <%= button_to remover_apontamento_consolidacao_consolidacao_entregas_path(@consolidacao, tracking_id: pilares.first.tracking_id, motorista: motorista),
remover_apontamento_consolidacao_consolidacao_entregas_path(@consolidacao, tracking_id: pilares.first.tracking_id, motorista: motorista),
method: :delete, method: :delete,
form: { data: { turbo_confirm: eh_termo ? "Remover a entrega de termo de #{motorista}?" : "Remover o apontamento da NF #{nf_exibida.presence || '—'} de #{motorista}?" } }, form: { data: { turbo_confirm: eh_termo ? "Remover a entrega de termo de #{motorista}?" : "Remover o apontamento da NF #{nf_exibida.presence || '—'} de #{motorista}?" } },
class: 'text-gray-400 hover:text-red-400 border border-[#2a2a2a] hover:border-red-500/50 px-3 py-2 rounded-lg text-xs min-h-[40px]' %> class: 'text-gray-400 hover:text-red-400 border border-[#2a2a2a] hover:border-red-500/50 px-3 py-2 rounded-lg text-xs min-h-[40px] inline-flex items-center gap-1.5' do %>
<%= icone :fechar, cor: nil, tamanho: 'w-3 h-3' %> Remover
<% end %>
</td> </td>
<% end %> <% end %>
</tr> </tr>
@@ -219,8 +230,8 @@
<div id="modal-preview" class="fixed inset-0 bg-black/80 z-50 hidden items-center justify-center p-4"> <div id="modal-preview" class="fixed inset-0 bg-black/80 z-50 hidden items-center justify-center p-4">
<div class="bg-[#1a1a1a] border border-[#2a2a2a] rounded-2xl max-w-2xl w-full max-h-[85vh] flex flex-col"> <div class="bg-[#1a1a1a] border border-[#2a2a2a] rounded-2xl max-w-2xl w-full max-h-[85vh] flex flex-col">
<div class="flex items-center justify-between px-6 py-4 border-b border-[#2a2a2a]"> <div class="flex items-center justify-between px-6 py-4 border-b border-[#2a2a2a]">
<h3 class="text-white font-bold">👁️ Pré-visualização do Extrato</h3> <h3 class="text-white font-bold flex items-center gap-2"><%= icone :visualizar %> Pré-visualização do Extrato</h3>
<button onclick="fecharPreview()" class="text-gray-400 hover:text-white text-2xl min-w-[44px] min-h-[44px]"></button> <button onclick="fecharPreview()" class="text-gray-400 hover:text-white min-w-[44px] min-h-[44px]" aria-label="Fechar"><%= icone :fechar, cor: nil, tamanho: 'w-5 h-5' %></button>
</div> </div>
<div id="modal-preview-conteudo" class="overflow-y-auto p-6 flex-1"> <div id="modal-preview-conteudo" class="overflow-y-auto p-6 flex-1">
<div class="flex justify-center py-10"><div class="spinner w-10 h-10"></div></div> <div class="flex justify-center py-10"><div class="spinner w-10 h-10"></div></div>
@@ -228,11 +239,11 @@
<div class="flex gap-3 px-6 py-4 border-t border-[#2a2a2a]"> <div class="flex gap-3 px-6 py-4 border-t border-[#2a2a2a]">
<a id="modal-confirmar" href="#" data-turbo="false" <a id="modal-confirmar" href="#" data-turbo="false"
class="flex-1 bg-orange-500 hover:bg-orange-600 text-black font-bold py-3 rounded-lg text-center min-h-[48px] flex items-center justify-center"> class="flex-1 bg-orange-500 hover:bg-orange-600 text-black font-bold py-3 rounded-lg text-center min-h-[48px] flex items-center justify-center">
Confirmar e baixar PDF <%= icone :confirmar, cor: nil %> Confirmar e baixar PDF
</a> </a>
<button onclick="fecharPreview()" <button onclick="fecharPreview()"
class="px-6 py-3 text-gray-400 hover:text-white border border-[#2a2a2a] rounded-lg min-h-[48px]"> class="px-6 py-3 text-gray-400 hover:text-white border border-[#2a2a2a] rounded-lg min-h-[48px]">
Voltar <%= icone :voltar, cor: nil, tamanho: 'w-4 h-4' %> Voltar
</button> </button>
</div> </div>
</div> </div>

View File

@@ -8,7 +8,7 @@
<%= link_to '← Voltar para o resumo', consolidacao_path(@consolidacao), class: 'text-gray-500 hover:text-orange-500 text-sm' %> <%= link_to '← Voltar para o resumo', consolidacao_path(@consolidacao), class: 'text-gray-500 hover:text-orange-500 text-sm' %>
<h1 class="text-2xl font-bold text-white mt-2"><%= @consolidacao.nome %></h1> <h1 class="text-2xl font-bold text-white mt-2"><%= @consolidacao.nome %></h1>
<p class="text-gray-400 text-sm"> <p class="text-gray-400 text-sm">
📅 <%= l @consolidacao.data_inicio, format: :short %> <%= l @consolidacao.data_fim, format: :short %> <%= icone :calendario %> <%= l @consolidacao.data_inicio, format: :short %> <%= icone :avancar, tamanho: 'w-3 h-3' %> <%= l @consolidacao.data_fim, format: :short %>
· <%= badge_status(@consolidacao.status) %> · <%= badge_status(@consolidacao.status) %>
</p> </p>
</div> </div>
@@ -42,20 +42,20 @@
<%# Botão único + menu suspenso %> <%# Botão único + menu suspenso %>
<button type="button" data-action="ferramentas#toggle" <button type="button" data-action="ferramentas#toggle"
class="w-full md:w-auto inline-flex items-center justify-center gap-2 bg-[#1a1a1a] hover:bg-[#2a2a2a] text-white border border-dashed border-[#3a3a3a] hover:border-orange-500 font-semibold px-4 py-3 rounded-lg min-h-[48px]"> class="w-full md:w-auto inline-flex items-center justify-center gap-2 bg-[#1a1a1a] hover:bg-[#2a2a2a] text-white border border-dashed border-[#3a3a3a] hover:border-orange-500 font-semibold px-4 py-3 rounded-lg min-h-[48px]">
Adicionar / Ferramentas <%= icone :adicionar %> Adicionar / Ferramentas
<span class="text-gray-500">▾</span> <%= icone :expandir, cor: 'text-gray-500' %>
</button> </button>
<div data-ferramentas-target="menu" <div data-ferramentas-target="menu"
class="hidden absolute z-40 mt-2 w-72 bg-[#1a1a1a] border border-[#2a2a2a] rounded-xl shadow-xl overflow-hidden"> class="hidden absolute z-40 mt-2 w-72 bg-[#1a1a1a] border border-[#2a2a2a] rounded-xl shadow-xl overflow-hidden">
<button type="button" data-action="apontamento#abrir ferramentas#fechar" <button type="button" data-action="apontamento#abrir ferramentas#fechar"
class="w-full text-left px-4 py-3 hover:bg-[#2a2a2a] border-l-2 border-transparent hover:border-orange-500"> class="w-full text-left px-4 py-3 hover:bg-[#2a2a2a] border-l-2 border-transparent hover:border-orange-500">
<span class="block text-white font-semibold">📝 Apontamento manual</span> <span class="block text-white font-semibold"><%= icone :apontamento %> Apontamento manual</span>
<span class="block text-gray-500 text-xs">nota que chegou por fora (busca pela NF)</span> <span class="block text-gray-500 text-xs">nota que chegou por fora (busca pela NF)</span>
</button> </button>
<button type="button" data-action="adicionar-motorista#abrir ferramentas#fechar" <button type="button" data-action="adicionar-motorista#abrir ferramentas#fechar"
class="w-full text-left px-4 py-3 hover:bg-[#2a2a2a] border-t border-[#2a2a2a] border-l-2 border-l-transparent hover:border-l-green-500"> class="w-full text-left px-4 py-3 hover:bg-[#2a2a2a] border-t border-[#2a2a2a] border-l-2 border-l-transparent hover:border-l-green-500">
<span class="block text-white font-semibold">🧑‍✈️ Adicionar motorista</span> <span class="block text-white font-semibold"><%= icone :motorista %> Adicionar motorista</span>
<span class="block text-gray-500 text-xs">incluir/restaurar um motorista nesta consolidação</span> <span class="block text-gray-500 text-xs">incluir/restaurar um motorista nesta consolidação</span>
</button> </button>
</div> </div>
@@ -67,7 +67,7 @@
<div class="flex items-center justify-between mb-1"> <div class="flex items-center justify-between mb-1">
<h2 class="text-xl font-bold text-white">Apontamento manual</h2> <h2 class="text-xl font-bold text-white">Apontamento manual</h2>
<button type="button" data-action="apontamento#fechar" <button type="button" data-action="apontamento#fechar"
class="text-gray-500 hover:text-white text-2xl leading-none"></button> class="text-gray-500 hover:text-white leading-none" aria-label="Fechar"><%= icone :fechar, cor: nil, tamanho: 'w-5 h-5' %></button>
</div> </div>
<p class="text-gray-400 text-sm mb-4">Busque a nota pela base de rastreio e adicione ao fechamento do motorista correspondente.</p> <p class="text-gray-400 text-sm mb-4">Busque a nota pela base de rastreio e adicione ao fechamento do motorista correspondente.</p>
@@ -89,22 +89,22 @@
<div data-apontamento-target="confirmar" class="hidden border-t border-[#2a2a2a] pt-4"> <div data-apontamento-target="confirmar" class="hidden border-t border-[#2a2a2a] pt-4">
<label class="block text-gray-400 text-sm mb-2">Classificar como <span class="text-gray-600">(pode marcar mais de uma)</span></label> <label class="block text-gray-400 text-sm mb-2">Classificar como <span class="text-gray-600">(pode marcar mais de uma)</span></label>
<div class="grid grid-cols-2 gap-2 mb-4"> <div class="grid grid-cols-2 gap-2 mb-4">
<% [['entrega_normal', '🚚 Entrega Normal', Configuracao.preco_entrega], <% [['entrega_normal', :caminhao, 'Entrega Normal', Configuracao.preco_entrega],
['retirada', '📦 Retirada', Configuracao.preco_retirada], ['retirada', :retirada, 'Retirada', Configuracao.preco_retirada],
['bonus', 'Bônus', Configuracao.preco_bonus], ['bonus', :bonus, 'Bônus', Configuracao.preco_bonus],
['desconto', '⚠️ Desconto', Configuracao.preco_desconto], ['desconto', :desconto, 'Desconto', Configuracao.preco_desconto],
['extraordinaria', 'Entrega Extraordinária', Configuracao.preco_extraordinaria]].each do |tipo, label, preco| %> ['extraordinaria', :extraordinaria, 'Entrega Extraordinária', Configuracao.preco_extraordinaria]].each do |tipo, ic, label, preco| %>
<label class="flex items-center gap-2 bg-[#0a0a0a] border border-[#2a2a2a] rounded-lg px-3 py-3 cursor-pointer hover:border-orange-500 text-sm text-white"> <label class="flex items-center gap-2 bg-[#0a0a0a] border border-[#2a2a2a] rounded-lg px-3 py-3 cursor-pointer hover:border-orange-500 text-sm text-white">
<input type="checkbox" value="<%= tipo %>" data-apontamento-target="tipoCheck" <input type="checkbox" value="<%= tipo %>" data-apontamento-target="tipoCheck"
<%= 'checked' if tipo == 'entrega_normal' %> <%= 'checked' if tipo == 'entrega_normal' %>
class="accent-orange-500"> class="accent-orange-500">
<span><%= label %><br><span class="text-gray-500 text-xs"><%= moeda(preco) %></span></span> <span><%= icone ic %> <%= label %><br><span class="text-gray-500 text-xs"><%= moeda(preco) %></span></span>
</label> </label>
<% end %> <% end %>
</div> </div>
<button type="button" data-action="apontamento#apontar" <button type="button" data-action="apontamento#apontar"
class="w-full bg-green-600 hover:bg-green-500 text-white font-bold py-3.5 rounded-lg min-h-[48px]"> class="w-full bg-green-600 hover:bg-green-500 text-white font-bold py-3.5 rounded-lg min-h-[48px]">
Adicionar apontamento Adicionar apontamento <%= icone :confirmar, cor: nil %>
</button> </button>
</div> </div>
</div> </div>
@@ -115,9 +115,9 @@
class="hidden fixed inset-0 z-50 bg-black/70 flex items-center justify-center p-4"> class="hidden fixed inset-0 z-50 bg-black/70 flex items-center justify-center p-4">
<div class="bg-[#1a1a1a] border border-[#2a2a2a] rounded-2xl w-full max-w-md p-6"> <div class="bg-[#1a1a1a] border border-[#2a2a2a] rounded-2xl w-full max-w-md p-6">
<div class="flex items-center justify-between mb-1"> <div class="flex items-center justify-between mb-1">
<h2 class="text-xl font-bold text-white">🧑‍✈️ Adicionar motorista</h2> <h2 class="text-xl font-bold text-white flex items-center gap-2"><%= icone :motorista %> Adicionar motorista</h2>
<button type="button" data-action="adicionar-motorista#fechar" <button type="button" data-action="adicionar-motorista#fechar"
class="text-gray-500 hover:text-white text-2xl leading-none"></button> class="text-gray-500 hover:text-white leading-none" aria-label="Fechar"><%= icone :fechar, cor: nil, tamanho: 'w-5 h-5' %></button>
</div> </div>
<p class="text-gray-400 text-sm mb-4">Inclua um motorista nesta consolidação. Se ele estava arquivado, será restaurado.</p> <p class="text-gray-400 text-sm mb-4">Inclua um motorista nesta consolidação. Se ele estava arquivado, será restaurado.</p>
@@ -128,7 +128,7 @@
class="w-full bg-[#0a0a0a] border border-[#2a2a2a] text-white rounded-lg px-3 py-3 text-base focus:border-orange-500 focus:outline-none mb-4"> class="w-full bg-[#0a0a0a] border border-[#2a2a2a] text-white rounded-lg px-3 py-3 text-base focus:border-orange-500 focus:outline-none mb-4">
<datalist id="motoristas-disponiveis"> <datalist id="motoristas-disponiveis">
<% Array(@motoristas_periodo).each do |m| %> <% Array(@motoristas_periodo).each do |m| %>
<option value="<%= m[:nome] %>" label="<%= '🚚 ' + m[:veiculos].join(', ') if m[:veiculos].any? %>"></option> <option value="<%= m[:nome] %>" label="<%= m[:veiculos].join(', ') if m[:veiculos].any? %>"></option>
<% end %> <% end %>
</datalist> </datalist>
@@ -141,7 +141,7 @@
class="w-full text-left px-3 py-2 bg-[#0a0a0a] border border-[#2a2a2a] hover:border-orange-500 rounded-lg"> class="w-full text-left px-3 py-2 bg-[#0a0a0a] border border-[#2a2a2a] hover:border-orange-500 rounded-lg">
<span class="block text-white text-sm font-semibold"><%= m[:nome] %></span> <span class="block text-white text-sm font-semibold"><%= m[:nome] %></span>
<% if m[:veiculos].any? %> <% if m[:veiculos].any? %>
<span class="block text-gray-500 text-xs">🚚 <%= m[:veiculos].join(', ') %></span> <span class="block text-gray-500 text-xs"><%= icone :caminhao %> <%= m[:veiculos].join(', ') %></span>
<% end %> <% end %>
</button> </button>
<% end %> <% end %>
@@ -152,7 +152,7 @@
<button type="submit" <button type="submit"
class="w-full bg-green-600 hover:bg-green-500 text-white font-bold py-3.5 rounded-lg min-h-[48px]"> class="w-full bg-green-600 hover:bg-green-500 text-white font-bold py-3.5 rounded-lg min-h-[48px]">
Adicionar Adicionar <%= icone :confirmar, cor: nil %>
</button> </button>
<% end %> <% end %>
</div> </div>
@@ -174,7 +174,7 @@
<div class="flex items-center gap-4"> <div class="flex items-center gap-4">
<div class="w-12 h-12 rounded-full flex items-center justify-center text-xl font-black <div class="w-12 h-12 rounded-full flex items-center justify-center text-xl font-black
<%= m[:completo] ? 'bg-green-600 text-white' : 'bg-orange-500 text-black' %>"> <%= m[:completo] ? 'bg-green-600 text-white' : 'bg-orange-500 text-black' %>">
<%= m[:completo] ? '✓' : m[:registro].motorista_nome.first.upcase %> <%= m[:completo] ? icone(:confirmar, cor: nil, tamanho: 'w-6 h-6') : m[:registro].motorista_nome.first.upcase %>
</div> </div>
<div> <div>
<p class="text-white font-bold text-lg"><%= m[:registro].motorista_nome %></p> <p class="text-white font-bold text-lg"><%= m[:registro].motorista_nome %></p>
@@ -184,13 +184,13 @@
<%= m[:classificadas] %>/<%= m[:total] %> classificadas <%= m[:classificadas] %>/<%= m[:total] %> classificadas
</span> </span>
<% if m[:apontamentos].positive? %> <% if m[:apontamentos].positive? %>
· <span class="text-orange-400"> <%= m[:apontamentos] %> apontamento<%= 's' if m[:apontamentos] > 1 %></span> · <span class="text-orange-400"><%= icone :adicionar, cor: nil, tamanho: 'w-3 h-3' %> <%= m[:apontamentos] %> apontamento<%= 's' if m[:apontamentos] > 1 %></span>
<% end %> <% end %>
</p> </p>
<% if m[:veiculos_total] > 0 %> <% if m[:veiculos_total] > 0 %>
<p class="text-xs mt-0.5"> <p class="text-xs mt-0.5">
<span class="<%= m[:veiculos_fechados] == m[:veiculos_total] ? 'text-green-400' : 'text-gray-400' %>"> <span class="<%= m[:veiculos_fechados] == m[:veiculos_total] ? 'text-green-400' : 'text-gray-400' %>">
🚗 <%= m[:veiculos_fechados] %>/<%= m[:veiculos_total] %> veículo<%= 's' if m[:veiculos_total] > 1 %> consolidado<%= 's' if m[:veiculos_fechados] != 1 %> <%= icone :veiculo, cor: nil %> <%= m[:veiculos_fechados] %>/<%= m[:veiculos_total] %> veículo<%= 's' if m[:veiculos_total] > 1 %> consolidado<%= 's' if m[:veiculos_fechados] != 1 %>
</span> </span>
<% if m[:veiculos_pendentes].any? %> <% if m[:veiculos_pendentes].any? %>
<span class="text-orange-400">· pendente<%= 's' if m[:veiculos_pendentes].size > 1 %>: <%= m[:veiculos_pendentes].join(', ') %></span> <span class="text-orange-400">· pendente<%= 's' if m[:veiculos_pendentes].size > 1 %>: <%= m[:veiculos_pendentes].join(', ') %></span>
@@ -201,7 +201,7 @@
</div> </div>
<div class="text-right <%= 'pr-10' if pode_arquivar_motorista %>"> <div class="text-right <%= 'pr-10' if pode_arquivar_motorista %>">
<p class="text-orange-500 font-bold"><%= moeda(m[:registro].valor_total) %></p> <p class="text-orange-500 font-bold"><%= moeda(m[:registro].valor_total) %></p>
<span class="text-gray-600 text-2xl">→</span> <%= icone :avancar, cor: 'text-gray-600', tamanho: 'w-5 h-5' %>
</div> </div>
</div> </div>
<div class="mt-3"> <div class="mt-3">
@@ -214,13 +214,14 @@
<% end %> <% end %>
<% if pode_arquivar_motorista %> <% if pode_arquivar_motorista %>
<%= button_to '🗄️', <%= button_to arquivar_motorista_consolidacao_path(@consolidacao, consolidacao_motorista_id: m[:registro].id),
arquivar_motorista_consolidacao_path(@consolidacao, consolidacao_motorista_id: m[:registro].id),
method: :post, method: :post,
form: { data: { turbo_confirm: "Arquivar #{m[:registro].motorista_nome} desta consolidação? Você poderá restaurar depois." }, form: { data: { turbo_confirm: "Arquivar #{m[:registro].motorista_nome} desta consolidação? Você poderá restaurar depois." },
class: 'absolute top-3 right-3' }, class: 'absolute top-3 right-3' },
class: 'text-gray-500 hover:text-red-400 border border-[#2a2a2a] hover:border-red-500/50 rounded-lg w-9 h-9 flex items-center justify-center', class: 'text-gray-500 hover:text-red-400 border border-[#2a2a2a] hover:border-red-500/50 rounded-lg w-9 h-9 flex items-center justify-center',
title: "Arquivar #{m[:registro].motorista_nome}" %> title: "Arquivar #{m[:registro].motorista_nome}" do %>
<%= icone :arquivar, cor: nil, tamanho: 'w-4 h-4' %>
<% end %>
<% end %> <% end %>
</div> </div>
<% end %> <% end %>
@@ -229,7 +230,7 @@
<%# Motoristas arquivados — restauráveis %> <%# Motoristas arquivados — restauráveis %>
<% if @arquivados.present? %> <% if @arquivados.present? %>
<div class="mt-6"> <div class="mt-6">
<h2 class="text-gray-400 font-semibold text-sm mb-2">🗄️ Motoristas arquivados (<%= @arquivados.size %>)</h2> <h2 class="text-gray-400 font-semibold text-sm mb-2 flex items-center gap-2"><%= icone :arquivar %> Motoristas arquivados (<%= @arquivados.size %>)</h2>
<div class="space-y-2"> <div class="space-y-2">
<% @arquivados.each do |cm| %> <% @arquivados.each do |cm| %>
<div class="flex items-center justify-between bg-[#141414] border border-[#2a2a2a] border-dashed rounded-xl px-4 py-3"> <div class="flex items-center justify-between bg-[#141414] border border-[#2a2a2a] border-dashed rounded-xl px-4 py-3">
@@ -239,17 +240,19 @@
</div> </div>
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<% if pode_arquivar_motorista %> <% if pode_arquivar_motorista %>
<%= button_to '♻️ Restaurar', <%= button_to desarquivar_motorista_consolidacao_path(@consolidacao, consolidacao_motorista_id: cm.id),
desarquivar_motorista_consolidacao_path(@consolidacao, consolidacao_motorista_id: cm.id),
method: :post, method: :post,
class: 'text-green-400 hover:text-green-300 border border-[#2a2a2a] hover:border-green-500/50 px-3 py-2 rounded-lg text-xs min-h-[40px]' %> class: 'text-green-400 hover:text-green-300 border border-[#2a2a2a] hover:border-green-500/50 px-3 py-2 rounded-lg text-xs min-h-[40px] inline-flex items-center gap-1.5' do %>
<%= icone :restaurar, cor: nil, tamanho: 'w-3.5 h-3.5' %> Restaurar
<% end %>
<% end %> <% end %>
<% if policy(@consolidacao).destroy? %> <% if policy(@consolidacao).destroy? %>
<%= button_to '🗑️ Excluir', <%= button_to excluir_motorista_consolidacao_path(@consolidacao, consolidacao_motorista_id: cm.id),
excluir_motorista_consolidacao_path(@consolidacao, consolidacao_motorista_id: cm.id),
method: :delete, method: :delete,
form: { data: { turbo_confirm: "Excluir DEFINITIVAMENTE #{cm.motorista_nome} desta consolidação? Os lançamentos dele serão apagados e isso não pode ser desfeito." } }, form: { data: { turbo_confirm: "Excluir DEFINITIVAMENTE #{cm.motorista_nome} desta consolidação? Os lançamentos dele serão apagados e isso não pode ser desfeito." } },
class: 'text-red-400 hover:text-red-300 border border-[#2a2a2a] hover:border-red-500/50 px-3 py-2 rounded-lg text-xs min-h-[40px]' %> class: 'text-red-400 hover:text-red-300 border border-[#2a2a2a] hover:border-red-500/50 px-3 py-2 rounded-lg text-xs min-h-[40px] inline-flex items-center gap-1.5' do %>
<%= icone :excluir, cor: nil, tamanho: 'w-3.5 h-3.5' %> Excluir
<% end %>
<% end %> <% end %>
</div> </div>
</div> </div>
@@ -272,25 +275,28 @@
<% if @consolidacao.rascunho? %> <% if @consolidacao.rascunho? %>
<div class="flex flex-col sm:flex-row gap-2"> <div class="flex flex-col sm:flex-row gap-2">
<% if !tudo_completo && tem_consolidados && tem_pendentes %> <% if !tudo_completo && tem_consolidados && tem_pendentes %>
<%= button_to '🔒 Arquivar pendentes e finalizar', <%= button_to finalizar_parcial_consolidacao_path(@consolidacao),
finalizar_parcial_consolidacao_path(@consolidacao),
method: :post, method: :post,
data: { turbo_confirm: 'Arquivar TODOS os veículos ainda não classificados 100% e finalizar? Eles ficam de fora desta consolidação (você resolve depois). Os veículos consolidados são pagos normalmente.' }, form: { data: { turbo_confirm: 'Arquivar TODOS os veículos ainda não classificados 100% e finalizar? Eles ficam de fora desta consolidação (você resolve depois). Os veículos consolidados são pagos normalmente.' } },
class: 'bg-orange-500 hover:bg-orange-600 text-black font-bold px-5 py-3.5 rounded-lg min-h-[48px] flex items-center justify-center text-center' %> class: 'bg-orange-500 hover:bg-orange-600 text-black font-bold px-5 py-3.5 rounded-lg min-h-[48px] flex items-center justify-center text-center gap-2' do %>
<%= icone :travado, cor: nil, tamanho: 'w-4 h-4' %> Arquivar pendentes e finalizar
<% end %> <% end %>
<%= button_to 'Finalizar consolidação ✓', finalizar_consolidacao_path(@consolidacao), <% end %>
<%= button_to finalizar_consolidacao_path(@consolidacao),
method: :post, method: :post,
disabled: !tudo_completo, disabled: !tudo_completo,
data: { turbo_confirm: 'Finalizar? Após finalizada, a consolidação não poderá ser editada por operadores.' }, form: { data: { turbo_confirm: 'Finalizar? Após finalizada, a consolidação não poderá ser editada por operadores.' } },
class: "font-bold px-6 py-3.5 rounded-lg min-h-[48px] transition-colors class: "font-bold px-6 py-3.5 rounded-lg min-h-[48px] transition-colors inline-flex items-center justify-center gap-2
#{tudo_completo ? 'bg-green-600 hover:bg-green-500 text-white cursor-pointer' : 'bg-[#2a2a2a] text-gray-600 cursor-not-allowed'}" %> #{tudo_completo ? 'bg-green-600 hover:bg-green-500 text-white cursor-pointer' : 'bg-[#2a2a2a] text-gray-600 cursor-not-allowed'}" do %>
Finalizar consolidação <%= icone :confirmar, cor: nil %>
<% end %>
</div> </div>
<% end %> <% end %>
</div> </div>
</div> </div>
<% unless tudo_completo %> <% unless tudo_completo %>
<p class="text-gray-500 text-sm mt-2 text-center"> <p class="text-gray-500 text-sm mt-2 text-center">
⚠️ Classifique todas as entregas de todos os motoristas para finalizar tudo<% if tem_consolidados && tem_pendentes %> — ou use <strong>“Arquivar pendentes e finalizar”</strong> para fechar só os veículos já consolidados.<% end %> <%= icone :alerta, cor: 'text-amber-400' %> Classifique todas as entregas de todos os motoristas para finalizar tudo<% if tem_consolidados && tem_pendentes %> — ou use <strong>“Arquivar pendentes e finalizar”</strong> para fechar só os veículos já consolidados.<% end %>
</p> </p>
<% end %> <% end %>
</div> </div>

View File

@@ -55,7 +55,7 @@
<%# Filtro de calendário — seleciona uma faixa de datas %> <%# Filtro de calendário — seleciona uma faixa de datas %>
<form id="dashboard-filtro-form" method="get" action="<%= dashboard_path %>" data-turbo="false" class="flex flex-wrap items-center gap-2"> <form id="dashboard-filtro-form" method="get" action="<%= dashboard_path %>" data-turbo="false" class="flex flex-wrap items-center gap-2">
<div class="relative"> <div class="relative">
<span class="pointer-events-none absolute right-3 top-1/2 -translate-y-1/2 text-orange-500">📅</span> <span class="pointer-events-none absolute right-3 top-1/2 -translate-y-1/2"><%= icone :calendario %></span>
<input type="text" id="periodo-range" readonly placeholder="Selecione o período" <input type="text" id="periodo-range" readonly placeholder="Selecione o período"
value="<%= @periodo_inicio.strftime('%d/%m/%Y') %> até <%= @periodo_fim.strftime('%d/%m/%Y') %>" value="<%= @periodo_inicio.strftime('%d/%m/%Y') %> até <%= @periodo_fim.strftime('%d/%m/%Y') %>"
class="cursor-pointer pl-4 pr-10 py-2.5 bg-[#1a1a1a] border border-white/10 rounded-xl class="cursor-pointer pl-4 pr-10 py-2.5 bg-[#1a1a1a] border border-white/10 rounded-xl
@@ -79,14 +79,14 @@
<% if @operacoes.any? %> <% if @operacoes.any? %>
<details class="relative" id="operacao-dropdown"> <details class="relative" id="operacao-dropdown">
<summary class="cursor-pointer list-none pl-4 pr-4 py-2.5 bg-[#1a1a1a] border border-white/10 rounded-xl text-white text-sm whitespace-nowrap hover:border-orange-500"> <summary class="cursor-pointer list-none pl-4 pr-4 py-2.5 bg-[#1a1a1a] border border-white/10 rounded-xl text-white text-sm whitespace-nowrap hover:border-orange-500">
🗂️ <%= @operacao_filtro.present? ? "#{@operacao_filtro.size} operação(ões)" : 'Todas as operações' %> <%= icone :pasta %> <%= @operacao_filtro.present? ? "#{@operacao_filtro.size} operação(ões)" : 'Todas as operações' %>
</summary> </summary>
<div class="absolute right-0 mt-2 z-30 w-72 max-h-80 overflow-y-auto bg-[#1a1a1a] border border-white/10 rounded-xl p-2 shadow-xl"> <div class="absolute right-0 mt-2 z-30 w-72 max-h-80 overflow-y-auto bg-[#1a1a1a] border border-white/10 rounded-xl p-2 shadow-xl">
<%# Limpar seleção — recarrega o dashboard sem filtro de operação %> <%# Limpar seleção — recarrega o dashboard sem filtro de operação %>
<div class="flex items-center justify-between px-2 pb-2 mb-1 border-b border-white/10"> <div class="flex items-center justify-between px-2 pb-2 mb-1 border-b border-white/10">
<span class="text-xs uppercase tracking-wider text-gray-400">Operações</span> <span class="text-xs uppercase tracking-wider text-gray-400">Operações</span>
<% if @operacao_filtro.present? %> <% if @operacao_filtro.present? %>
<%= link_to '✕ Limpar', dashboard_path(inicio: @periodo_inicio.strftime('%Y-%m-%d'), fim: @periodo_fim.strftime('%Y-%m-%d')), <%= link_to rotulo(:fechar, 'Limpar', cor: nil, tamanho: 'w-3 h-3'), dashboard_path(inicio: @periodo_inicio.strftime('%Y-%m-%d'), fim: @periodo_fim.strftime('%Y-%m-%d')),
data: { turbo: false }, class: 'text-xs text-orange-400 hover:text-orange-300 font-semibold' %> data: { turbo: false }, class: 'text-xs text-orange-400 hover:text-orange-300 font-semibold' %>
<% end %> <% end %>
</div> </div>
@@ -107,7 +107,7 @@
<% end %> <% end %>
<%# Imprimir relatório financeiro do período/operação atuais %> <%# Imprimir relatório financeiro do período/operação atuais %>
<%= link_to '🖨️ Relatório', <%= link_to rotulo(:imprimir, 'Relatório'),
dashboard_relatorio_financeiro_path( dashboard_relatorio_financeiro_path(
inicio: @periodo_inicio.strftime('%Y-%m-%d'), inicio: @periodo_inicio.strftime('%Y-%m-%d'),
fim: @periodo_fim.strftime('%Y-%m-%d'), fim: @periodo_fim.strftime('%Y-%m-%d'),
@@ -124,7 +124,7 @@
<div class="sm:col-span-2 xl:col-span-1 bg-[#f97316] rounded-2xl p-6 relative overflow-hidden"> <div class="sm:col-span-2 xl:col-span-1 bg-[#f97316] rounded-2xl p-6 relative overflow-hidden">
<div class="absolute inset-0 bg-gradient-to-br from-orange-400/20 to-transparent"></div> <div class="absolute inset-0 bg-gradient-to-br from-orange-400/20 to-transparent"></div>
<div class="relative"> <div class="relative">
<p class="text-orange-100 text-sm font-medium mb-2">💰 Valor Estimado</p> <p class="text-orange-100 text-sm font-medium mb-2 flex items-center gap-2"><%= icone :dinheiro, cor: nil %> Valor Estimado</p>
<p class="text-4xl font-black text-white mb-1"> <p class="text-4xl font-black text-white mb-1">
<%= moeda(@valor_estimado) %> <%= moeda(@valor_estimado) %>
</p> </p>
@@ -136,7 +136,7 @@
<%# Card destacado: total consolidado e pago (consolidações finalizadas do período) %> <%# Card destacado: total consolidado e pago (consolidações finalizadas do período) %>
<div class="sm:col-span-2 xl:col-span-1 bg-[#1a1a1a] rounded-2xl border border-green-500/30 p-6"> <div class="sm:col-span-2 xl:col-span-1 bg-[#1a1a1a] rounded-2xl border border-green-500/30 p-6">
<p class="text-gray-400 text-sm font-medium mb-2">💼 Consolidado / Pago</p> <p class="text-gray-400 text-sm font-medium mb-2 flex items-center gap-2"><%= icone :consolidado_pago %> Consolidado / Pago</p>
<p class="text-3xl font-black text-white mb-1"><%= moeda(@fin_custo_total) %></p> <p class="text-3xl font-black text-white mb-1"><%= moeda(@fin_custo_total) %></p>
<p class="text-gray-400 text-xs mb-2">consolidado no período</p> <p class="text-gray-400 text-xs mb-2">consolidado no período</p>
<div class="flex items-center gap-2 text-sm border-t border-white/5 pt-2"> <div class="flex items-center gap-2 text-sm border-t border-white/5 pt-2">
@@ -147,7 +147,7 @@
<%# Card 2: Total de entregas %> <%# Card 2: Total de entregas %>
<div class="bg-[#1a1a1a] rounded-2xl border border-white/5 p-6"> <div class="bg-[#1a1a1a] rounded-2xl border border-white/5 p-6">
<p class="text-gray-400 text-sm font-medium mb-2">📦 Total Entregas</p> <p class="text-gray-400 text-sm font-medium mb-2 flex items-center gap-2"><%= icone :consolidacoes %> Total Entregas</p>
<p class="text-4xl font-black text-white mb-1"><%= @total_entregas %></p> <p class="text-4xl font-black text-white mb-1"><%= @total_entregas %></p>
<div class="flex flex-wrap items-center gap-x-2 gap-y-1 text-sm"> <div class="flex flex-wrap items-center gap-x-2 gap-y-1 text-sm">
<span class="text-green-400"><%= @entregas_pagas %> entregues</span> <span class="text-green-400"><%= @entregas_pagas %> entregues</span>
@@ -160,7 +160,7 @@
<%# Card 3: Consolidações %> <%# Card 3: Consolidações %>
<div class="bg-[#1a1a1a] rounded-2xl border border-white/5 p-6"> <div class="bg-[#1a1a1a] rounded-2xl border border-white/5 p-6">
<p class="text-gray-400 text-sm font-medium mb-2">📋 Consolidações</p> <p class="text-gray-400 text-sm font-medium mb-2 flex items-center gap-2"><%= icone :planilha %> Consolidações</p>
<p class="text-4xl font-black text-white mb-1"><%= @consolidacoes_mes.count %></p> <p class="text-4xl font-black text-white mb-1"><%= @consolidacoes_mes.count %></p>
<div class="flex items-center gap-2 text-sm"> <div class="flex items-center gap-2 text-sm">
<span class="text-yellow-500"><%= @consolidacoes_abertas %> abertas</span> <span class="text-yellow-500"><%= @consolidacoes_abertas %> abertas</span>
@@ -171,7 +171,7 @@
<%# Card 4: Motoristas ativos %> <%# Card 4: Motoristas ativos %>
<div class="bg-[#1a1a1a] rounded-2xl border border-white/5 p-6"> <div class="bg-[#1a1a1a] rounded-2xl border border-white/5 p-6">
<p class="text-gray-400 text-sm font-medium mb-2">🚗 Motoristas</p> <p class="text-gray-400 text-sm font-medium mb-2 flex items-center gap-2"><%= icone :veiculo %> Motoristas</p>
<p class="text-4xl font-black text-white mb-1"><%= @motoristas.count %></p> <p class="text-4xl font-black text-white mb-1"><%= @motoristas.count %></p>
<p class="text-gray-400 text-sm">com entregas no período</p> <p class="text-gray-400 text-sm">com entregas no período</p>
</div> </div>
@@ -180,22 +180,22 @@
<%# KPIs FINANCEIROS (consolidações finalizadas, já com filtro de operação) %> <%# KPIs FINANCEIROS (consolidações finalizadas, já com filtro de operação) %>
<div class="grid grid-cols-1 sm:grid-cols-2 xl:grid-cols-4 gap-4"> <div class="grid grid-cols-1 sm:grid-cols-2 xl:grid-cols-4 gap-4">
<div class="bg-[#1a1a1a] rounded-2xl border border-white/5 p-6"> <div class="bg-[#1a1a1a] rounded-2xl border border-white/5 p-6">
<p class="text-gray-400 text-sm font-medium mb-2">💰 Custo total</p> <p class="text-gray-400 text-sm font-medium mb-2 flex items-center gap-2"><%= icone :dinheiro %> Custo total</p>
<p class="text-3xl font-black text-white"><%= moeda(@fin_custo_total) %></p> <p class="text-3xl font-black text-white"><%= moeda(@fin_custo_total) %></p>
<p class="text-gray-400 text-xs mt-1"><%= @fin_qtd_entregas %> entregas classificadas</p> <p class="text-gray-400 text-xs mt-1"><%= @fin_qtd_entregas %> entregas classificadas</p>
</div> </div>
<div class="bg-[#1a1a1a] rounded-2xl border border-white/5 p-6"> <div class="bg-[#1a1a1a] rounded-2xl border border-white/5 p-6">
<p class="text-gray-400 text-sm font-medium mb-2">🎫 Ticket médio</p> <p class="text-gray-400 text-sm font-medium mb-2 flex items-center gap-2"><%= icone :ticket %> Ticket médio</p>
<p class="text-3xl font-black text-white"><%= moeda(@fin_ticket_medio) %></p> <p class="text-3xl font-black text-white"><%= moeda(@fin_ticket_medio) %></p>
<p class="text-gray-400 text-xs mt-1">por entrega</p> <p class="text-gray-400 text-xs mt-1">por entrega</p>
</div> </div>
<div class="bg-[#1a1a1a] rounded-2xl border border-white/5 p-6"> <div class="bg-[#1a1a1a] rounded-2xl border border-white/5 p-6">
<p class="text-gray-400 text-sm font-medium mb-2">✅ Pago</p> <p class="text-gray-400 text-sm font-medium mb-2 flex items-center gap-2"><%= icone :sucesso, cor: 'text-green-500' %> Pago</p>
<p class="text-3xl font-black text-green-400"><%= moeda(@fin_pago) %></p> <p class="text-3xl font-black text-green-400"><%= moeda(@fin_pago) %></p>
<p class="text-gray-400 text-xs mt-1">já confirmado</p> <p class="text-gray-400 text-xs mt-1">já confirmado</p>
</div> </div>
<div class="bg-[#1a1a1a] rounded-2xl border border-white/5 p-6"> <div class="bg-[#1a1a1a] rounded-2xl border border-white/5 p-6">
<p class="text-gray-400 text-sm font-medium mb-2">🕗 A pagar</p> <p class="text-gray-400 text-sm font-medium mb-2 flex items-center gap-2"><%= icone :relogio, cor: 'text-amber-400' %> A pagar</p>
<p class="text-3xl font-black text-yellow-500"><%= moeda(@fin_pendente) %></p> <p class="text-3xl font-black text-yellow-500"><%= moeda(@fin_pendente) %></p>
<p class="text-gray-400 text-xs mt-1">pendente</p> <p class="text-gray-400 text-xs mt-1">pendente</p>
</div> </div>
@@ -238,7 +238,7 @@
</div> </div>
<% else %> <% else %>
<div class="flex-1 min-h-48 flex flex-col items-center justify-center text-gray-400"> <div class="flex-1 min-h-48 flex flex-col items-center justify-center text-gray-400">
<div class="text-3xl mb-2">💸</div> <div class="mb-2"><%= icone :custo, tamanho: 'w-8 h-8', cor: 'text-gray-600' %></div>
<p class="text-sm">Nenhum pagamento realizado neste período</p> <p class="text-sm">Nenhum pagamento realizado neste período</p>
<% if @pag_pend_valor.positive? %> <% if @pag_pend_valor.positive? %>
<p class="text-xs mt-1"> <p class="text-xs mt-1">
@@ -252,7 +252,7 @@
<%# Top motoristas %> <%# Top motoristas %>
<div class="bg-[#1a1a1a] rounded-2xl border border-white/5 p-6"> <div class="bg-[#1a1a1a] rounded-2xl border border-white/5 p-6">
<h2 class="text-lg font-semibold text-white mb-4">🏆 Motoristas</h2> <h2 class="text-lg font-semibold text-white mb-4 flex items-center gap-2"><%= icone :ranking %> Motoristas</h2>
<% if @motoristas.any? %> <% if @motoristas.any? %>
<div class="space-y-3"> <div class="space-y-3">
@@ -282,7 +282,7 @@
</div> </div>
<% else %> <% else %>
<div class="text-center py-8 text-gray-400"> <div class="text-center py-8 text-gray-400">
<div class="text-3xl mb-2">🚚</div> <div class="mb-2"><%= icone :caminhao, tamanho: 'w-8 h-8', cor: 'text-gray-600' %></div>
<p class="text-sm">Nenhuma entrega concluída neste período</p> <p class="text-sm">Nenhuma entrega concluída neste período</p>
</div> </div>
<% end %> <% end %>
@@ -294,34 +294,34 @@
<%# Custo por operação (barra) %> <%# Custo por operação (barra) %>
<div class="bg-[#1a1a1a] rounded-2xl border border-white/5 p-6"> <div class="bg-[#1a1a1a] rounded-2xl border border-white/5 p-6">
<h2 class="text-lg font-semibold text-white mb-1">🏷️ Custo por operação</h2> <h2 class="text-lg font-semibold text-white mb-1 flex items-center gap-2"><%= icone :tag %> Custo por operação</h2>
<p class="text-gray-400 text-sm mb-4">Valor consolidado no período</p> <p class="text-gray-400 text-sm mb-4">Valor consolidado no período</p>
<% if @fin_por_operacao.any? %> <% if @fin_por_operacao.any? %>
<div class="relative h-64"><canvas id="grafico-custo-operacao"></canvas></div> <div class="relative h-64"><canvas id="grafico-custo-operacao"></canvas></div>
<% else %> <% else %>
<div class="text-center py-8 text-gray-400"><div class="text-3xl mb-2">🏷️</div><p class="text-sm">Sem dados no período</p></div> <div class="text-center py-8 text-gray-400"><div class="mb-2"><%= icone :tag, tamanho: 'w-8 h-8', cor: 'text-gray-600' %></div><p class="text-sm">Sem dados no período</p></div>
<% end %> <% end %>
</div> </div>
<%# Composição por tipo (rosca) %> <%# Composição por tipo (rosca) %>
<div class="bg-[#1a1a1a] rounded-2xl border border-white/5 p-6"> <div class="bg-[#1a1a1a] rounded-2xl border border-white/5 p-6">
<h2 class="text-lg font-semibold text-white mb-1">🧩 Composição do custo</h2> <h2 class="text-lg font-semibold text-white mb-1 flex items-center gap-2"><%= icone :composicao %> Composição do custo</h2>
<p class="text-gray-400 text-sm mb-4">Por tipo de lançamento</p> <p class="text-gray-400 text-sm mb-4">Por tipo de lançamento</p>
<% if @fin_por_tipo.any? %> <% if @fin_por_tipo.any? %>
<div class="relative h-64"><canvas id="grafico-composicao-tipo"></canvas></div> <div class="relative h-64"><canvas id="grafico-composicao-tipo"></canvas></div>
<% else %> <% else %>
<div class="text-center py-8 text-gray-400"><div class="text-3xl mb-2">🧩</div><p class="text-sm">Sem dados no período</p></div> <div class="text-center py-8 text-gray-400"><div class="mb-2"><%= icone :composicao, tamanho: 'w-8 h-8', cor: 'text-gray-600' %></div><p class="text-sm">Sem dados no período</p></div>
<% end %> <% end %>
</div> </div>
<%# Custo por motorista (barra) %> <%# Custo por motorista (barra) %>
<div class="bg-[#1a1a1a] rounded-2xl border border-white/5 p-6"> <div class="bg-[#1a1a1a] rounded-2xl border border-white/5 p-6">
<h2 class="text-lg font-semibold text-white mb-1">👤 Custo por motorista</h2> <h2 class="text-lg font-semibold text-white mb-1 flex items-center gap-2"><%= icone :pessoa %> Custo por motorista</h2>
<p class="text-gray-400 text-sm mb-4">Por motorista no período</p> <p class="text-gray-400 text-sm mb-4">Por motorista no período</p>
<% if @fin_por_motorista.any? %> <% if @fin_por_motorista.any? %>
<div class="relative h-64"><canvas id="grafico-custo-motorista"></canvas></div> <div class="relative h-64"><canvas id="grafico-custo-motorista"></canvas></div>
<% else %> <% else %>
<div class="text-center py-8 text-gray-400"><div class="text-3xl mb-2">👤</div><p class="text-sm">Sem dados no período</p></div> <div class="text-center py-8 text-gray-400"><div class="mb-2"><%= icone :pessoa, tamanho: 'w-8 h-8', cor: 'text-gray-600' %></div><p class="text-sm">Sem dados no período</p></div>
<% end %> <% end %>
</div> </div>
</div> </div>
@@ -331,12 +331,12 @@
<%# Rosca pago vs a pagar (por valor) %> <%# Rosca pago vs a pagar (por valor) %>
<div class="bg-[#1a1a1a] rounded-2xl border border-white/5 p-6"> <div class="bg-[#1a1a1a] rounded-2xl border border-white/5 p-6">
<h2 class="text-lg font-semibold text-white mb-1">💵 Pagamentos</h2> <h2 class="text-lg font-semibold text-white mb-1 flex items-center gap-2"><%= icone :pagamento %> Pagamentos</h2>
<p class="text-gray-400 text-sm mb-4">Pagos por data de pagamento · pendentes no período</p> <p class="text-gray-400 text-sm mb-4">Pagos por data de pagamento · pendentes no período</p>
<% if (@pag_pago_qtd + @pag_pend_qtd).zero? %> <% if (@pag_pago_qtd + @pag_pend_qtd).zero? %>
<div class="text-center py-8 text-gray-400"> <div class="text-center py-8 text-gray-400">
<div class="text-3xl mb-2">🧾</div> <div class="mb-2"><%= icone :nota_fiscal, tamanho: 'w-8 h-8', cor: 'text-gray-600' %></div>
<p class="text-sm">Nenhum pagamento ou pendência no período</p> <p class="text-sm">Nenhum pagamento ou pendência no período</p>
</div> </div>
<% else %> <% else %>
@@ -365,7 +365,7 @@
<%# Pagamentos realizados no período (filtrados por pago_em) %> <%# Pagamentos realizados no período (filtrados por pago_em) %>
<div> <div>
<h2 class="text-lg font-semibold text-white mb-4">✅ Pagamentos realizados no período</h2> <h2 class="text-lg font-semibold text-white mb-4 flex items-center gap-2"><%= icone :sucesso, cor: 'text-green-500' %> Pagamentos realizados no período</h2>
<% if @pagamentos_feitos.any? %> <% if @pagamentos_feitos.any? %>
<div class="overflow-x-auto"> <div class="overflow-x-auto">
<table class="w-full text-sm"> <table class="w-full text-sm">
@@ -405,7 +405,7 @@
<%# Pendentes (consolidações finalizadas do período sem pagamento) %> <%# Pendentes (consolidações finalizadas do período sem pagamento) %>
<div> <div>
<h2 class="text-lg font-semibold text-white mb-4">🕗 Pagamentos pendentes</h2> <h2 class="text-lg font-semibold text-white mb-4 flex items-center gap-2"><%= icone :relogio, cor: 'text-amber-400' %> Pagamentos pendentes</h2>
<% if @pagamentos_pendentes.any? %> <% if @pagamentos_pendentes.any? %>
<div class="overflow-x-auto"> <div class="overflow-x-auto">
<table class="w-full text-sm"> <table class="w-full text-sm">
@@ -435,7 +435,7 @@
<div class="flex gap-1" id="pagamentos-pendentes-abas"></div> <div class="flex gap-1" id="pagamentos-pendentes-abas"></div>
</div> </div>
<% else %> <% else %>
<p class="text-gray-400 text-sm py-3">Nenhuma pendência no período. 🎉</p> <p class="text-gray-400 text-sm py-3">Nenhuma pendência no período. <%= icone :sucesso, cor: 'text-green-500' %></p>
<% end %> <% end %>
</div> </div>
</div> </div>
@@ -444,7 +444,7 @@
<%# Por operação %> <%# Por operação %>
<% if @por_operacao.any? %> <% if @por_operacao.any? %>
<div class="bg-[#1a1a1a] rounded-2xl border border-white/5 p-6"> <div class="bg-[#1a1a1a] rounded-2xl border border-white/5 p-6">
<h2 class="text-lg font-semibold text-white mb-4">🏥 Entregas por Local</h2> <h2 class="text-lg font-semibold text-white mb-4 flex items-center gap-2"><%= icone :operacao %> Entregas por Local</h2>
<div class="grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-6 gap-3"> <div class="grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-6 gap-3">
<% total_op = @por_operacao.values.sum.to_f %> <% total_op = @por_operacao.values.sum.to_f %>
<% @por_operacao.each do |local, qtd| %> <% @por_operacao.each do |local, qtd| %>

View File

@@ -6,7 +6,7 @@
<div class="text-center mb-8"> <div class="text-center mb-8">
<div class="w-16 h-16 bg-[#f97316] rounded-2xl flex items-center justify-center mx-auto mb-3"> <div class="w-16 h-16 bg-[#f97316] rounded-2xl flex items-center justify-center mx-auto mb-3">
<span class="text-3xl">🔒</span> <%= icone :travado, tamanho: 'w-8 h-8' %>
</div> </div>
<h1 class="text-white font-bold text-2xl">Definir nova senha</h1> <h1 class="text-white font-bold text-2xl">Definir nova senha</h1>
</div> </div>

View File

@@ -6,7 +6,7 @@
<div class="text-center mb-8"> <div class="text-center mb-8">
<div class="w-16 h-16 bg-[#f97316] rounded-2xl flex items-center justify-center mx-auto mb-3"> <div class="w-16 h-16 bg-[#f97316] rounded-2xl flex items-center justify-center mx-auto mb-3">
<span class="text-3xl">🔑</span> <%= icone :chave, tamanho: 'w-8 h-8' %>
</div> </div>
<h1 class="text-white font-bold text-2xl">Recuperar senha</h1> <h1 class="text-white font-bold text-2xl">Recuperar senha</h1>
<p class="text-gray-400 text-sm mt-1">Informe seu e-mail para receber as instruções</p> <p class="text-gray-400 text-sm mt-1">Informe seu e-mail para receber as instruções</p>
@@ -38,7 +38,7 @@
</div> </div>
<p class="text-gray-600 text-xs text-center mt-6"> <p class="text-gray-600 text-xs text-center mt-6">
⚠️ O envio de e-mail depende da configuração SMTP no servidor.<br> <%= icone :alerta, cor: 'text-amber-400' %> O envio de e-mail depende da configuração SMTP no servidor.<br>
Sem SMTP, peça ao administrador para redefinir sua senha. Sem SMTP, peça ao administrador para redefinir sua senha.
</p> </p>

View File

@@ -25,12 +25,12 @@
<button id="tab-email" <button id="tab-email"
onclick="switchTab('email')" onclick="switchTab('email')"
class="flex-1 py-4 text-sm font-medium transition-colors tab-active text-[#f97316] border-b-2 border-[#f97316]"> class="flex-1 py-4 text-sm font-medium transition-colors tab-active text-[#f97316] border-b-2 border-[#f97316]">
📧 E-mail <%= icone :email, cor: nil %> E-mail
</button> </button>
<button id="tab-pin" <button id="tab-pin"
onclick="switchTab('pin')" onclick="switchTab('pin')"
class="flex-1 py-4 text-sm font-medium transition-colors text-gray-400 hover:text-white border-b-2 border-transparent"> class="flex-1 py-4 text-sm font-medium transition-colors text-gray-400 hover:text-white border-b-2 border-transparent">
🔑 PIN Motorista <%= icone :chave, cor: nil %> PIN Motorista
</button> </button>
</div> </div>
@@ -114,7 +114,7 @@
class="h-14 rounded-xl bg-[#0a0a0a] border border-white/10 text-gray-400 class="h-14 rounded-xl bg-[#0a0a0a] border border-white/10 text-gray-400
text-sm hover:bg-red-900/40 hover:border-red-500/50 hover:text-red-400 text-sm hover:bg-red-900/40 hover:border-red-500/50 hover:text-red-400
active:scale-95 transition-all"> active:scale-95 transition-all">
<%= icone :fechar, cor: nil, tamanho: 'w-4 h-4' %>
</button> </button>
<button type="button" <button type="button"
onclick="pinPress('0')" onclick="pinPress('0')"

View File

@@ -36,31 +36,31 @@
<%# Dashboard — admin/gerente/operador/externo (todos menos motorista) %> <%# Dashboard — admin/gerente/operador/externo (todos menos motorista) %>
<% unless current_user.motorista? %> <% unless current_user.motorista? %>
<%= nav_link_to '📊 Dashboard', dashboard_path %> <%= nav_link_to 'Dashboard', dashboard_path, icon: :dashboard %>
<%= nav_link_to '📈 Operações', operacoes_dashboard_path %> <%= nav_link_to 'Operações', operacoes_dashboard_path, icon: :operacoes %>
<% end %> <% end %>
<%# Consolidações — só quem pode consolidar (exclui o externo) %> <%# Consolidações — só quem pode consolidar (exclui o externo) %>
<% if current_user.pode_consolidar? %> <% if current_user.pode_consolidar? %>
<%= nav_link_to '📦 Consolidações', consolidacoes_path %> <%= nav_link_to 'Consolidações', consolidacoes_path, icon: :consolidacoes %>
<%= nav_link_to '📁 Arquivadas', arquivadas_consolidacoes_path %> <%= nav_link_to 'Arquivadas', arquivadas_consolidacoes_path, icon: :arquivadas %>
<% end %> <% end %>
<%# Motorista %> <%# Motorista %>
<% if current_user.motorista? %> <% if current_user.motorista? %>
<%= nav_link_to '🏠 Meu Painel', motorista_dashboard_path %> <%= nav_link_to 'Meu Painel', motorista_dashboard_path, icon: :painel %>
<% end %> <% end %>
<%# Admin/Gerente — Configurações %> <%# Admin/Gerente — Configurações %>
<% if current_user.pode_ver_config? %> <% if current_user.pode_ver_config? %>
<div class="pt-4 mt-4 border-t border-[#2a2a2a]"> <div class="pt-4 mt-4 border-t border-[#2a2a2a]">
<p class="px-3 text-xs text-gray-600 uppercase tracking-wider mb-2">Administração</p> <p class="px-3 text-xs text-gray-600 uppercase tracking-wider mb-2">Administração</p>
<%= nav_link_to '👥 Usuários', admin_usuarios_path %> <%= nav_link_to 'Usuários', admin_usuarios_path, icon: :usuarios %>
<%= nav_link_to '⚙️ Configurações', admin_configuracoes_path %> <%= nav_link_to 'Configurações', admin_configuracoes_path, icon: :configuracoes %>
<%= nav_link_to '🔍 Auditoria', admin_auditoria_logs_path %> <%= nav_link_to 'Auditoria', admin_auditoria_logs_path, icon: :auditoria %>
<%= nav_link_to '📥 Planilha SimpliRoute', admin_planilha_simpli_route_path %> <%= nav_link_to 'Planilha SimpliRoute', admin_planilha_simpli_route_path, icon: :planilha %>
<% if current_user.admin? %> <% if current_user.admin? %>
<%= nav_link_to '✏️ Editar Lançamento', admin_edicao_lancamento_path %> <%= nav_link_to 'Editar Lançamento', admin_edicao_lancamento_path, icon: :editar_lancamento %>
<% end %> <% end %>
</div> </div>
<% end %> <% end %>
@@ -70,7 +70,7 @@
<div class="px-6 py-4 border-t border-[#2a2a2a]"> <div class="px-6 py-4 border-t border-[#2a2a2a]">
<%= link_to destroy_user_session_path, data: { turbo_method: :delete, turbo_confirm: 'Deseja sair?' }, <%= link_to destroy_user_session_path, data: { turbo_method: :delete, turbo_confirm: 'Deseja sair?' },
class: "flex items-center gap-2 text-gray-400 hover:text-red-400 text-sm transition-colors" do %> class: "flex items-center gap-2 text-gray-400 hover:text-red-400 text-sm transition-colors" do %>
<span>🚪</span> Sair <%= icone :sair, cor: nil, tamanho: 'w-4 h-4' %> Sair
<% end %> <% end %>
</div> </div>
</aside> </aside>
@@ -86,15 +86,19 @@
group-hover:border group-hover:border-orange-500/70 group-hover:border group-hover:border-orange-500/70
focus:w-7 focus:h-16 focus:bg-[#1a1a1a] focus:text-orange-500" focus:w-7 focus:h-16 focus:bg-[#1a1a1a] focus:text-orange-500"
title="Esconder/mostrar menu" aria-label="Esconder ou mostrar o menu"> title="Esconder/mostrar menu" aria-label="Esconder ou mostrar o menu">
<span id="sidebar-toggle-icon" class="text-lg leading-none">◀</span> <%# Gira 180° quando a sidebar está recolhida (ver syncSidebarToggleIcon) %>
<span id="sidebar-toggle-icon" class="leading-none transition-transform duration-200">
<%= icone :recolher, cor: nil, tamanho: 'w-4 h-4' %>
</span>
</button> </button>
</div> </div>
<%# Topbar mobile %> <%# Topbar mobile %>
<header class="md:hidden fixed top-0 left-0 right-0 z-30 bg-[#111111] border-b border-[#2a2a2a] px-4 py-3 flex items-center justify-between"> <header class="md:hidden fixed top-0 left-0 right-0 z-30 bg-[#111111] border-b border-[#2a2a2a] px-4 py-3 flex items-center justify-between">
<button onclick="toggleSidebar()" <button onclick="toggleSidebar()"
class="text-orange-500 text-2xl min-h-[48px] min-w-[48px] flex items-center justify-center"> class="text-orange-500 min-h-[48px] min-w-[48px] flex items-center justify-center"
aria-label="Abrir menu">
<%= icone :menu, cor: nil, tamanho: 'w-7 h-7' %>
</button> </button>
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<%= image_tag '/logo-reem.png', alt: 'Reem Transporte', class: 'h-8 w-auto object-contain' %> <%= image_tag '/logo-reem.png', alt: 'Reem Transporte', class: 'h-8 w-auto object-contain' %>
@@ -121,8 +125,8 @@
} }
function syncSidebarToggleIcon() { function syncSidebarToggleIcon() {
const icon = document.getElementById('sidebar-toggle-icon'); const icon = document.getElementById('sidebar-toggle-icon');
if (icon) icon.textContent = if (icon) icon.classList.toggle('rotate-180',
document.documentElement.classList.contains('sidebar-collapsed') ? '▶' : '◀'; document.documentElement.classList.contains('sidebar-collapsed'));
} }
syncSidebarToggleIcon(); syncSidebarToggleIcon();
</script> </script>

View File

@@ -18,11 +18,11 @@
<nav class="flex-1 px-3 py-4 space-y-1 overflow-y-auto scrollbar-hidden"> <nav class="flex-1 px-3 py-4 space-y-1 overflow-y-auto scrollbar-hidden">
<%# Dashboard (todos exceto motorista) %> <%# Dashboard (todos exceto motorista) %>
<%= nav_link_to 'Dashboard', dashboard_path, icon: '📊' %> <%= nav_link_to 'Dashboard', dashboard_path, icon: :dashboard %>
<%# Consolidações (admin, gerente, operador) %> <%# Consolidações (admin, gerente, operador) %>
<% if current_user.pode_criar_consolidacao? %> <% if current_user.pode_criar_consolidacao? %>
<%= nav_link_to 'Consolidações', consolidacoes_path, icon: '📋' %> <%= nav_link_to 'Consolidações', consolidacoes_path, icon: :consolidacoes %>
<% end %> <% end %>
<%# Separador Admin %> <%# Separador Admin %>
@@ -31,8 +31,8 @@
<p class="px-4 text-xs font-semibold text-gray-600 uppercase tracking-wider">Administração</p> <p class="px-4 text-xs font-semibold text-gray-600 uppercase tracking-wider">Administração</p>
</div> </div>
<%= nav_link_to 'Usuários', usuarios_path, icon: '👥' %> <%= nav_link_to 'Usuários', usuarios_path, icon: :usuarios %>
<%= nav_link_to 'Configurações', configuracoes_path, icon: '⚙️' %> <%= nav_link_to 'Configurações', configuracoes_path, icon: :configuracoes %>
<% end %> <% end %>
<%# PDFs (fase 7 — link desabilitado por enquanto) %> <%# PDFs (fase 7 — link desabilitado por enquanto) %>
@@ -41,7 +41,7 @@
<p class="px-4 text-xs font-semibold text-gray-600 uppercase tracking-wider">Relatórios</p> <p class="px-4 text-xs font-semibold text-gray-600 uppercase tracking-wider">Relatórios</p>
</div> </div>
<div class="flex items-center gap-3 px-4 py-3 rounded-xl text-sm font-medium text-gray-600 cursor-not-allowed"> <div class="flex items-center gap-3 px-4 py-3 rounded-xl text-sm font-medium text-gray-600 cursor-not-allowed">
<span class="text-lg w-5 text-center">📄</span> <%= icone :documento, cor: nil, tamanho: 'w-5 h-5' %>
<span>Relatórios PDF</span> <span>Relatórios PDF</span>
<span class="ml-auto text-[10px] bg-gray-800 text-gray-500 px-1.5 py-0.5 rounded">Em breve</span> <span class="ml-auto text-[10px] bg-gray-800 text-gray-500 px-1.5 py-0.5 rounded">Em breve</span>
</div> </div>

View File

@@ -100,7 +100,7 @@
</video> </video>
<button type="button" onclick="event.stopPropagation(); encerrarSplash()" <button type="button" onclick="event.stopPropagation(); encerrarSplash()"
class="absolute bottom-6 right-6 text-gray-400 hover:text-white text-sm border border-white/20 rounded-lg px-4 py-2"> class="absolute bottom-6 right-6 text-gray-400 hover:text-white text-sm border border-white/20 rounded-lg px-4 py-2">
Pular Pular <%= icone :fechar, cor: nil, tamanho: 'w-3.5 h-3.5' %>
</button> </button>
</div> </div>
<script> <script>
@@ -130,7 +130,7 @@
<div id="flash-notice" <div id="flash-notice"
class="fixed top-4 right-4 z-50 bg-orange-500 text-black font-semibold px-5 py-3 rounded-lg shadow-lg flex items-center gap-2 animate-fade-in" class="fixed top-4 right-4 z-50 bg-orange-500 text-black font-semibold px-5 py-3 rounded-lg shadow-lg flex items-center gap-2 animate-fade-in"
data-controller="flash"> data-controller="flash">
<span>✅</span> <%= notice %> <%= icone :sucesso, cor: nil, tamanho: 'w-5 h-5' %> <%= notice %>
</div> </div>
<% end %> <% end %>
@@ -138,7 +138,7 @@
<div id="flash-alert" <div id="flash-alert"
class="fixed top-4 right-4 z-50 bg-red-600 text-white font-semibold px-5 py-3 rounded-lg shadow-lg flex items-center gap-2" class="fixed top-4 right-4 z-50 bg-red-600 text-white font-semibold px-5 py-3 rounded-lg shadow-lg flex items-center gap-2"
data-controller="flash"> data-controller="flash">
<span>⚠️</span> <%= alert %> <%= icone :alerta, cor: nil, tamanho: 'w-5 h-5' %> <%= alert %>
</div> </div>
<% end %> <% end %>

View File

@@ -3,13 +3,13 @@
<div class="max-w-2xl mx-auto"> <div class="max-w-2xl mx-auto">
<h1 class="text-3xl font-bold text-white mb-1">Olá, <%= current_user.nome.split.first %>! 👋</h1> <h1 class="text-3xl font-bold text-white mb-1">Olá, <%= current_user.nome.split.first %>!</h1>
<p class="text-gray-300 text-lg mb-6">Aqui estão seus valores de entregas.</p> <p class="text-gray-300 text-lg mb-6">Aqui estão seus valores de entregas.</p>
<%# ── Aviso de pagamento recém-confirmado ───────────────── %> <%# ── Aviso de pagamento recém-confirmado ───────────────── %>
<% if @pagamentos_recentes.any? %> <% if @pagamentos_recentes.any? %>
<div class="bg-green-600/15 border border-green-600/40 rounded-2xl p-5 mb-6"> <div class="bg-green-600/15 border border-green-600/40 rounded-2xl p-5 mb-6">
<p class="text-green-400 font-bold uppercase tracking-wide mb-3">✅ Pagamento confirmado!</p> <p class="text-green-400 font-bold uppercase tracking-wide mb-3 flex items-center gap-2"><%= icone :sucesso, cor: nil %> Pagamento confirmado!</p>
<div class="space-y-3"> <div class="space-y-3">
<% @pagamentos_recentes.each do |cm| %> <% @pagamentos_recentes.each do |cm| %>
<div class="flex flex-col gap-0.5"> <div class="flex flex-col gap-0.5">
@@ -18,9 +18,9 @@
pela consolidação <span class="font-semibold"><%= cm.consolidacao.nome %></span> pela consolidação <span class="font-semibold"><%= cm.consolidacao.nome %></span>
</p> </p>
<p class="text-gray-300 text-sm"> <p class="text-gray-300 text-sm">
💰 <%= l cm.pago_em.to_date, format: :short %> <%= icone :dinheiro %> <%= l cm.pago_em.to_date, format: :short %>
<% if cm.forma_pagamento.present? %>· <%= cm.forma_pagamento.humanize %><% end %> <% if cm.forma_pagamento.present? %>· <%= cm.forma_pagamento.humanize %><% end %>
<% if cm.nota_fiscal.present? %>· 🧾 NF <%= cm.nota_fiscal %><% end %> <% if cm.nota_fiscal.present? %>· <%= icone :nota_fiscal %> NF <%= cm.nota_fiscal %><% end %>
</p> </p>
</div> </div>
<% end %> <% end %>
@@ -33,20 +33,20 @@
<%# Card 1 — Valor Estimado (LARANJA) %> <%# Card 1 — Valor Estimado (LARANJA) %>
<div class="bg-orange-500 rounded-2xl p-6"> <div class="bg-orange-500 rounded-2xl p-6">
<p class="text-black/80 font-bold uppercase tracking-wide">💰 Valor estimado deste mês</p> <p class="text-black/80 font-bold uppercase tracking-wide flex items-center gap-2"><%= icone :dinheiro, cor: nil %> Valor estimado deste mês</p>
<p class="text-black/70 text-sm mt-1">📅 <%= l @estimado_inicio, format: :short %> a <%= l @estimado_fim, format: :short %></p> <p class="text-black/70 text-sm mt-1"><%= icone :calendario, cor: nil %> <%= l @estimado_inicio, format: :short %> a <%= l @estimado_fim, format: :short %></p>
<p class="text-black font-black text-4xl sm:text-5xl mt-2 leading-tight whitespace-nowrap"><%= moeda(@valor_estimado) %></p> <p class="text-black font-black text-4xl sm:text-5xl mt-2 leading-tight whitespace-nowrap"><%= moeda(@valor_estimado) %></p>
<p class="text-black/80 text-base mt-2"> <p class="text-black/80 text-base mt-2">
<%= @entregas_mes %> entregas feitas e confirmadas <%= @entregas_mes %> entregas feitas e confirmadas
</p> </p>
<p class="text-black/60 text-sm mt-3"> <p class="text-black/60 text-sm mt-3">
⚠️ Valor aproximado. O valor certo sai quando a empresa fechar o pagamento. <%= icone :alerta, cor: nil %> Valor aproximado. O valor certo sai quando a empresa fechar o pagamento.
</p> </p>
</div> </div>
<%# Card 2 — Valor Consolidado (BRANCO) %> <%# Card 2 — Valor Consolidado (BRANCO) %>
<div class="bg-white rounded-2xl p-6"> <div class="bg-white rounded-2xl p-6">
<p class="text-gray-600 font-bold uppercase tracking-wide">✅ Valor fechado para pagamento</p> <p class="text-gray-600 font-bold uppercase tracking-wide flex items-center gap-2"><%= icone :sucesso, cor: 'text-green-600' %> Valor fechado para pagamento</p>
<p class="text-black font-black text-4xl sm:text-5xl mt-2 leading-tight whitespace-nowrap"><%= moeda(@valor_consolidado) %></p> <p class="text-black font-black text-4xl sm:text-5xl mt-2 leading-tight whitespace-nowrap"><%= moeda(@valor_consolidado) %></p>
<p class="text-gray-600 text-base mt-2"> <p class="text-gray-600 text-base mt-2">
Soma dos pagamentos já confirmados pela empresa Soma dos pagamentos já confirmados pela empresa
@@ -55,7 +55,7 @@
</div> </div>
<%# ── Lista de consolidações ──────────────────────────── %> <%# ── Lista de consolidações ──────────────────────────── %>
<h2 class="text-white font-bold text-xl mb-3">📋 Meus pagamentos</h2> <h2 class="text-white font-bold text-xl mb-3 flex items-center gap-2"><%= icone :planilha %> Meus pagamentos</h2>
<% if @minhas_consolidacoes.any? %> <% if @minhas_consolidacoes.any? %>
<div class="space-y-3"> <div class="space-y-3">
@@ -72,27 +72,27 @@
<% end %> <% end %>
</div> </div>
<p class="text-gray-300 text-base mt-1"> <p class="text-gray-300 text-base mt-1">
📅 <%= l c.data_inicio, format: :short %> a <%= l c.data_fim, format: :short %> <%= icone :calendario %> <%= l c.data_inicio, format: :short %> a <%= l c.data_fim, format: :short %>
</p> </p>
<p class="text-orange-500 font-black text-3xl mt-1 whitespace-nowrap"><%= moeda(cm&.valor_total || 0) %></p> <p class="text-orange-500 font-black text-3xl mt-1 whitespace-nowrap"><%= moeda(cm&.valor_total || 0) %></p>
<% if cm&.pago? %> <% if cm&.pago? %>
<p class="text-green-400 text-sm mt-1"> <p class="text-green-400 text-sm mt-1">
💰 Pago em <%= l cm.pago_em.to_date, format: :short %> <%= icone :dinheiro, cor: 'text-green-400' %> Pago em <%= l cm.pago_em.to_date, format: :short %>
<% if cm.forma_pagamento.present? %>via <%= cm.forma_pagamento.humanize %><% end %> <% if cm.forma_pagamento.present? %>via <%= cm.forma_pagamento.humanize %><% end %>
</p> </p>
<% if cm.nota_fiscal.present? %> <% if cm.nota_fiscal.present? %>
<p class="text-gray-300 text-sm mt-0.5">🧾 Nota Fiscal: <%= cm.nota_fiscal %></p> <p class="text-gray-300 text-sm mt-0.5"><%= icone :nota_fiscal %> Nota Fiscal: <%= cm.nota_fiscal %></p>
<% end %> <% end %>
<% end %> <% end %>
</div> </div>
<% if c.finalizada? %> <% if c.finalizada? %>
<%= link_to '⬇️ Baixar meu extrato', <%= link_to rotulo(:baixar, 'Baixar meu extrato', cor: nil),
gerar_pdf_extrato_consolidacao_path(c, motorista: current_user.nome), gerar_pdf_extrato_consolidacao_path(c, motorista: current_user.nome),
data: { turbo: false }, data: { turbo: false },
class: 'w-full md:w-auto bg-orange-500 hover:bg-orange-600 text-black font-bold text-base px-6 py-4 rounded-xl min-h-[52px] flex items-center justify-center text-center' %> class: 'w-full md:w-auto bg-orange-500 hover:bg-orange-600 text-black font-bold text-base px-6 py-4 rounded-xl min-h-[52px] flex items-center justify-center text-center' %>
<% else %> <% else %>
<span class="w-full md:w-auto text-gray-300 text-base px-5 py-4 border border-[#2a2a2a] rounded-xl text-center"> <span class="w-full md:w-auto text-gray-300 text-base px-5 py-4 border border-[#2a2a2a] rounded-xl text-center">
Aguardando fechamento <%= icone :parcial, cor: 'text-amber-400' %> Aguardando fechamento
</span> </span>
<% end %> <% end %>
</div> </div>
@@ -101,7 +101,7 @@
</div> </div>
<% else %> <% else %>
<div class="bg-[#1a1a1a] border border-[#2a2a2a] rounded-2xl p-10 text-center"> <div class="bg-[#1a1a1a] border border-[#2a2a2a] rounded-2xl p-10 text-center">
<p class="text-5xl mb-3">📭</p> <p class="mb-3"><%= icone :vazio, tamanho: 'w-12 h-12', cor: 'text-gray-600' %></p>
<p class="text-gray-200 text-lg">Você ainda não aparece em nenhum pagamento.</p> <p class="text-gray-200 text-lg">Você ainda não aparece em nenhum pagamento.</p>
<p class="text-gray-400 text-base mt-1">Quando a empresa fechar um período com suas entregas, ele aparece aqui.</p> <p class="text-gray-400 text-base mt-1">Quando a empresa fechar um período com suas entregas, ele aparece aqui.</p>
</div> </div>

View File

@@ -43,13 +43,13 @@
</button> </button>
<button type="submit" id="btn-entrar" disabled <button type="submit" id="btn-entrar" disabled
class="bg-orange-500 disabled:bg-[#2a2a2a] disabled:text-gray-600 text-black font-bold text-xl rounded-xl py-5 min-h-[64px] transition-colors"> class="bg-orange-500 disabled:bg-[#2a2a2a] disabled:text-gray-600 text-black font-bold text-xl rounded-xl py-5 min-h-[64px] transition-colors">
<%= icone :confirmar, cor: nil, tamanho: 'w-6 h-6' %>
</button> </button>
</div> </div>
<% end %> <% end %>
<p class="text-gray-300 text-base text-center mt-6"> <p class="text-gray-300 text-base text-center mt-6">
💡 Você também pode escanear o QR Code do seu extrato para entrar direto. <%= icone :dica %> Você também pode escanear o QR Code do seu extrato para entrar direto.
</p> </p>
<p class="text-center mt-4"> <p class="text-center mt-4">
<%= link_to 'Sou administrador/gerente →', new_user_session_path, class: 'text-orange-500 hover:text-orange-400 text-sm' %> <%= link_to 'Sou administrador/gerente →', new_user_session_path, class: 'text-orange-500 hover:text-orange-400 text-sm' %>

View File

@@ -18,7 +18,7 @@
<div class="bg-[#1a1a1a] rounded-2xl border border-white/5 p-6"> <div class="bg-[#1a1a1a] rounded-2xl border border-white/5 p-6">
<div class="flex flex-wrap items-center justify-between gap-3 mb-4"> <div class="flex flex-wrap items-center justify-between gap-3 mb-4">
<div> <div>
<h2 class="text-lg font-semibold text-white">📋 Planilha da operação</h2> <h2 class="text-lg font-semibold text-white flex items-center gap-2"><%= icone :planilha %> Planilha da operação</h2>
<p class="text-gray-400 text-sm">Espelho por NF — pesquise por nota, motorista, unidade, ocorrência, veículo...</p> <p class="text-gray-400 text-sm">Espelho por NF — pesquise por nota, motorista, unidade, ocorrência, veículo...</p>
</div> </div>
@@ -27,7 +27,7 @@
<input type="hidden" name="<%= k %>" value="<%= v %>"> <input type="hidden" name="<%= k %>" value="<%= v %>">
<% end %> <% end %>
<div class="relative"> <div class="relative">
<span class="pointer-events-none absolute left-3 top-1/2 -translate-y-1/2 text-gray-500">🔎</span> <span class="pointer-events-none absolute left-3 top-1/2 -translate-y-1/2"><%= icone :buscar, cor: 'text-gray-500' %></span>
<input type="search" name="q" value="<%= @busca %>" placeholder="Pesquisar..." <input type="search" name="q" value="<%= @busca %>" placeholder="Pesquisar..."
class="pl-9 pr-3 py-2 bg-[#0a0a0a] border border-white/10 rounded-xl text-white text-sm class="pl-9 pr-3 py-2 bg-[#0a0a0a] border border-white/10 rounded-xl text-white text-sm
w-52 sm:w-72 focus:outline-none focus:border-orange-500 placeholder-gray-500"> w-52 sm:w-72 focus:outline-none focus:border-orange-500 placeholder-gray-500">
@@ -37,7 +37,7 @@
Buscar Buscar
</button> </button>
<% if @busca.present? %> <% if @busca.present? %>
<%= link_to '✕ Limpar', operacoes_planilha_path(request.query_parameters.except('q', 'pg')), <%= link_to rotulo(:fechar, 'Limpar', cor: nil, tamanho: 'w-3 h-3'), operacoes_planilha_path(request.query_parameters.except('q', 'pg')),
data: { turbo: false }, class: 'text-xs text-orange-400 hover:text-orange-300 whitespace-nowrap' %> data: { turbo: false }, class: 'text-xs text-orange-400 hover:text-orange-300 whitespace-nowrap' %>
<% end %> <% end %>
</form> </form>

View File

@@ -12,12 +12,12 @@
</div> </div>
<div class="map-toggle inline-flex rounded-lg overflow-hidden border border-white/10 text-xs"> <div class="map-toggle inline-flex rounded-lg overflow-hidden border border-white/10 text-xs">
<button type="button" data-view="heat" <button type="button" data-view="heat"
class="px-3 py-1.5 bg-orange-500 text-black font-semibold">🔥 Calor</button> class="px-3 py-1.5 bg-orange-500 text-black font-semibold"><%= icone :calor, cor: nil, tamanho: 'w-3.5 h-3.5' %> Calor</button>
<button type="button" data-view="pontos" <button type="button" data-view="pontos"
class="px-3 py-1.5 text-gray-300 hover:text-white">📍 Pontos (<%= pontos.size %>)</button> class="px-3 py-1.5 text-gray-300 hover:text-white"><%= icone :local, cor: nil, tamanho: 'w-3.5 h-3.5' %> Pontos (<%= pontos.size %>)</button>
</div> </div>
<button type="button" class="map-fullscreen px-3 py-1.5 rounded-lg border border-white/10 text-gray-300 hover:text-white text-xs" title="Ver em tela cheia"> <button type="button" class="map-fullscreen px-3 py-1.5 rounded-lg border border-white/10 text-gray-300 hover:text-white text-xs" title="Ver em tela cheia">
Tela cheia <%= icone :tela_cheia, cor: nil, tamanho: 'w-3.5 h-3.5' %> Tela cheia
</button> </button>
</div> </div>
</div> </div>
@@ -30,7 +30,7 @@
data-pontos="<%= pontos.to_json %>" data-pontos="<%= pontos.to_json %>"
data-centro="<%= metricas.centro_mapa.to_json %>"></div> data-centro="<%= metricas.centro_mapa.to_json %>"></div>
<p class="text-xs text-gray-600 mt-2"> <p class="text-xs text-gray-600 mt-2">
🔥 Calor mostra a densidade · 📍 Pontos mostra cada entrega (clique no marcador para detalhes; coordenada de check-out). <%= icone :calor %> Calor mostra a densidade · <%= icone :local %> Pontos mostra cada entrega (clique no marcador para detalhes; coordenada de check-out).
</p> </p>
<% end %> <% end %>
</div> </div>

View File

@@ -25,7 +25,7 @@
<p class="text-gray-400 text-sm font-medium mb-1">Total de Entregas</p> <p class="text-gray-400 text-sm font-medium mb-1">Total de Entregas</p>
<p class="text-6xl font-black text-white leading-none inline-block origin-left transition-transform duration-200 group-hover:scale-110"><%= metricas.total %></p> <p class="text-6xl font-black text-white leading-none inline-block origin-left transition-transform duration-200 group-hover:scale-110"><%= metricas.total %></p>
<p class="text-xs text-gray-500 mt-2 h-0 overflow-hidden opacity-0 transition-all duration-200 group-hover:h-4 group-hover:opacity-100"> <p class="text-xs text-gray-500 mt-2 h-0 overflow-hidden opacity-0 transition-all duration-200 group-hover:h-4 group-hover:opacity-100">
✅ <%= metricas.sucesso %> · <span class="text-blue-500">●</span> <%= metricas.recusas %> · ⏳ <%= metricas.pendentes %> <%= icone :sucesso, cor: 'text-green-500', tamanho: 'w-3.5 h-3.5' %> <%= metricas.sucesso %> · <%= icone :ponto, cor: 'text-blue-500', tamanho: 'w-3.5 h-3.5' %> <%= metricas.recusas %> · <%= icone :parcial, cor: 'text-amber-400', tamanho: 'w-3.5 h-3.5' %> <%= metricas.pendentes %>
</p> </p>
</div> </div>
<div class="group bg-[#1a1a1a] rounded-2xl border border-white/5 p-7 transition-all duration-200 hover:border-green-500/40 hover:bg-[#202020] hover:-translate-y-0.5 hover:shadow-lg hover:shadow-black/40"> <div class="group bg-[#1a1a1a] rounded-2xl border border-white/5 p-7 transition-all duration-200 hover:border-green-500/40 hover:bg-[#202020] hover:-translate-y-0.5 hover:shadow-lg hover:shadow-black/40">

View File

@@ -30,7 +30,7 @@
data: { turbo: false }, data: { turbo: false },
title: (ativo ? 'Clique para remover o filtro' : "Filtrar por #{cel}"), title: (ativo ? 'Clique para remover o filtro' : "Filtrar por #{cel}"),
class: "inline-flex items-center gap-1 hover:text-orange-400 #{'text-orange-400 font-semibold' if ativo}" do %> class: "inline-flex items-center gap-1 hover:text-orange-400 #{'text-orange-400 font-semibold' if ativo}" do %>
<%= cel %><% if ativo %> <span aria-hidden="true">✕</span><% end %> <%= cel %><% if ativo %> <%= icone :fechar, cor: nil, tamanho: 'w-3 h-3' %><% end %>
<% end %> <% end %>
<% else %> <% else %>
<%= cel %> <%= cel %>

View File

@@ -52,9 +52,9 @@
<h1 class="text-2xl font-bold text-white">Dashboard de Operações</h1> <h1 class="text-2xl font-bold text-white">Dashboard de Operações</h1>
<p class="text-gray-400 text-sm mt-0.5"> <p class="text-gray-400 text-sm mt-0.5">
<% if @modo == 'global' %> <% if @modo == 'global' %>
🌐 Global · <%= @periodo_inicio.strftime('%d/%m/%Y') %> <%= @periodo_fim.strftime('%d/%m/%Y') %> <%= icone :global %> Global · <%= @periodo_inicio.strftime('%d/%m/%Y') %> <%= @periodo_fim.strftime('%d/%m/%Y') %>
<% elsif @modo == 'comparar' %> <% elsif @modo == 'comparar' %>
⚖️ Comparação entre operações (cada uma no seu próprio período) <%= icone :comparar %> Comparação entre operações (cada uma no seu próprio período)
<% elsif @metricas&.data_inicio %> <% elsif @metricas&.data_inicio %>
<%= @metricas.operacoes_label %> · <%= @metricas.data_inicio.strftime('%d/%m/%Y') %> <%= @metricas.data_fim.strftime('%d/%m/%Y') %> <%= @metricas.operacoes_label %> · <%= @metricas.data_inicio.strftime('%d/%m/%Y') %> <%= @metricas.data_fim.strftime('%d/%m/%Y') %>
<% elsif @operacao %> <% elsif @operacao %>
@@ -69,7 +69,7 @@
<% if @modo == 'global' %> <% if @modo == 'global' %>
<div class="flex flex-wrap items-center gap-2"> <div class="flex flex-wrap items-center gap-2">
<div class="relative"> <div class="relative">
<span class="pointer-events-none absolute right-3 top-1/2 -translate-y-1/2 text-orange-500">📅</span> <span class="pointer-events-none absolute right-3 top-1/2 -translate-y-1/2"><%= icone :calendario %></span>
<input type="text" id="periodo-range" readonly placeholder="Selecione o período" <input type="text" id="periodo-range" readonly placeholder="Selecione o período"
value="<%= @periodo_inicio.strftime('%d/%m/%Y') %> até <%= @periodo_fim.strftime('%d/%m/%Y') %>" value="<%= @periodo_inicio.strftime('%d/%m/%Y') %> até <%= @periodo_fim.strftime('%d/%m/%Y') %>"
class="cursor-pointer pl-4 pr-10 py-2.5 bg-[#1a1a1a] border border-white/10 rounded-xl class="cursor-pointer pl-4 pr-10 py-2.5 bg-[#1a1a1a] border border-white/10 rounded-xl
@@ -89,12 +89,12 @@
</div> </div>
<%# ── Abas de modo + atalho para a planilha ─────────────── %> <%# ── Abas de modo + atalho para a planilha ─────────────── %>
<% modos = { 'operacao' => '🏥 Operação', 'global' => '🌐 Global', 'comparar' => '⚖️ Comparar' } %> <% modos = { 'operacao' => [:operacao, 'Operação'], 'global' => [:global, 'Global'], 'comparar' => [:comparar, 'Comparar'] } %>
<div class="flex flex-wrap items-center justify-between gap-2 border-b border-white/5 pb-3"> <div class="flex flex-wrap items-center justify-between gap-2 border-b border-white/5 pb-3">
<div class="flex flex-wrap items-center gap-2"> <div class="flex flex-wrap items-center gap-2">
<% modos.each do |m, label| %> <% modos.each do |m, (ic, label)| %>
<% ativo = @modo == m %> <% ativo = @modo == m %>
<%= link_to label, <%= link_to rotulo(ic, label, cor: (ativo ? nil : 'text-brand-laranja')),
operacoes_dashboard_path(ctx_ops.merge(modo: m, inicio: ini_iso, fim: fim_iso)), operacoes_dashboard_path(ctx_ops.merge(modo: m, inicio: ini_iso, fim: fim_iso)),
data: { turbo: false }, data: { turbo: false },
class: "px-4 py-2 rounded-xl text-sm font-semibold whitespace-nowrap #{ativo ? 'bg-orange-500 text-black' : 'bg-[#1a1a1a] text-gray-300 border border-white/10 hover:border-orange-500'}" %> class: "px-4 py-2 rounded-xl text-sm font-semibold whitespace-nowrap #{ativo ? 'bg-orange-500 text-black' : 'bg-[#1a1a1a] text-gray-300 border border-white/10 hover:border-orange-500'}" %>
@@ -106,7 +106,7 @@
.slice('f_driver', 'f_sts', 'f_status', 'f_obs', 'f_resultado', 'f_data') .slice('f_driver', 'f_sts', 'f_status', 'f_obs', 'f_resultado', 'f_data')
.merge(modo: (@modo == 'global' ? 'global' : 'operacao'), inicio: ini_iso, fim: fim_iso) %> .merge(modo: (@modo == 'global' ? 'global' : 'operacao'), inicio: ini_iso, fim: fim_iso) %>
<% planilha_ctx[:operacao] = @operacao if @operacao %> <% planilha_ctx[:operacao] = @operacao if @operacao %>
<%= link_to '📋 Planilha da operação', operacoes_planilha_path(planilha_ctx), <%= link_to rotulo(:planilha, 'Planilha da operação', cor: nil), operacoes_planilha_path(planilha_ctx),
data: { turbo: false }, data: { turbo: false },
class: 'px-4 py-2 rounded-xl text-sm font-semibold whitespace-nowrap bg-[#1a1a1a] class: 'px-4 py-2 rounded-xl text-sm font-semibold whitespace-nowrap bg-[#1a1a1a]
text-orange-400 border border-orange-500/40 hover:bg-orange-500 hover:text-black' %> text-orange-400 border border-orange-500/40 hover:bg-orange-500 hover:text-black' %>
@@ -125,7 +125,7 @@
<% @chips.each do |chip| %> <% @chips.each do |chip| %>
<span class="inline-flex items-center gap-2 px-3 py-1.5 rounded-full bg-orange-500/15 border border-orange-500/30 text-orange-300 text-xs"> <span class="inline-flex items-center gap-2 px-3 py-1.5 rounded-full bg-orange-500/15 border border-orange-500/30 text-orange-300 text-xs">
<%= chip[:rotulo] %>: <strong class="text-white"><%= chip[:display] || chip[:valor] %></strong> <%= chip[:rotulo] %>: <strong class="text-white"><%= chip[:display] || chip[:valor] %></strong>
<%= link_to '✕', operacoes_dashboard_path(request.query_parameters.except(chip[:param])), <%= link_to icone(:fechar, cor: nil, tamanho: 'w-3 h-3'), operacoes_dashboard_path(request.query_parameters.except(chip[:param])),
data: { turbo: false }, class: 'hover:text-white font-bold', title: 'Remover filtro' %> data: { turbo: false }, class: 'hover:text-white font-bold', title: 'Remover filtro' %>
</span> </span>
<% end %> <% end %>
@@ -160,7 +160,7 @@
<% elsif @modo == 'global' %> <% elsif @modo == 'global' %>
<div class="bg-[#1a1a1a] rounded-2xl border border-white/5 p-4 text-sm text-gray-300"> <div class="bg-[#1a1a1a] rounded-2xl border border-white/5 p-4 text-sm text-gray-300">
🌐 Visão <strong class="text-white">Global</strong> — todas as <%= icone :global %> Visão <strong class="text-white">Global</strong> — todas as
<%= @tabelas_validas.size %> operações agregadas no período. <%= @tabelas_validas.size %> operações agregadas no período.
</div> </div>
<% if @metricas %> <% if @metricas %>
@@ -208,7 +208,7 @@
<%# Detalhe completo de cada operação (recolhível) %> <%# Detalhe completo de cada operação (recolhível) %>
<details class="bg-[#1a1a1a] rounded-2xl border border-white/5"> <details class="bg-[#1a1a1a] rounded-2xl border border-white/5">
<summary class="cursor-pointer px-6 py-4 text-sm text-gray-300 hover:text-white select-none"> <summary class="cursor-pointer px-6 py-4 text-sm text-gray-300 hover:text-white select-none">
🔎 Ver detalhe completo de cada operação <%= icone :buscar %> Ver detalhe completo de cada operação
</summary> </summary>
<div class="grid grid-cols-1 xl:grid-cols-2 gap-6 p-4 pt-0"> <div class="grid grid-cols-1 xl:grid-cols-2 gap-6 p-4 pt-0">
<div> <div>
@@ -442,6 +442,12 @@
}); });
} }
// Ícone do sprite (public/icons.svg) — mesmos SVG usados no resto do sistema.
function ico(id, cor) {
return '<svg width="12" height="12" fill="' + (cor || '#f97316') + '" style="vertical-align:-1px">' +
'<use href="/icons.svg#' + id + '"></use></svg>';
}
function popupHtml(p) { function popupHtml(p) {
const linha = (icone, val) => val ? ('<div>' + icone + ' ' + escapeHtml(val) + '</div>') : ''; const linha = (icone, val) => val ? ('<div>' + icone + ' ' + escapeHtml(val) + '</div>') : '';
const foto = p.foto const foto = p.foto
@@ -449,22 +455,22 @@
'<img src="' + escapeHtml(p.foto) + '" alt="Fachada" loading="lazy" ' + '<img src="' + escapeHtml(p.foto) + '" alt="Fachada" loading="lazy" ' +
'onerror="this.style.display=\'none\';this.parentNode.nextElementSibling.style.display=\'block\'" ' + 'onerror="this.style.display=\'none\';this.parentNode.nextElementSibling.style.display=\'block\'" ' +
'style="margin-top:8px;width:100%;max-height:160px;object-fit:cover;border-radius:6px;display:block"></a>' + 'style="margin-top:8px;width:100%;max-height:160px;object-fit:cover;border-radius:6px;display:block"></a>' +
'<div style="display:none;margin-top:6px;color:#9ca3af;font-size:11px">📷 Foto indisponível (link expirado/sem acesso)</div>' '<div style="display:none;margin-top:6px;color:#9ca3af;font-size:11px">' + ico('camera-fill', '#9ca3af') + ' Foto indisponível (link expirado/sem acesso)</div>'
: '<div style="margin-top:6px;color:#6b7280;font-size:11px">📷 Sem foto da fachada para esta entrega</div>'; : '<div style="margin-top:6px;color:#6b7280;font-size:11px">' + ico('camera-fill', '#6b7280') + ' Sem foto da fachada para esta entrega</div>';
const statusLinha = p.sucesso const statusLinha = p.sucesso
? '<div style="color:#16a34a;font-size:11px;margin-bottom:6px"> Entrega realizada</div>' ? '<div style="color:#16a34a;font-size:11px;margin-bottom:6px">' + ico('check-lg', '#16a34a') + ' Entrega realizada</div>'
: '<div style="color:#3b82f6;font-size:11px;margin-bottom:6px"> Insucesso' + : '<div style="color:#3b82f6;font-size:11px;margin-bottom:6px">' + ico('x-lg', '#3b82f6') + ' Insucesso' +
(p.motivo ? ' — ' + escapeHtml(p.motivo) : '') + '</div>'; (p.motivo ? ' — ' + escapeHtml(p.motivo) : '') + '</div>';
return '' + return '' +
'<div style="min-width:210px;max-width:240px">' + '<div style="min-width:210px;max-width:240px">' +
'<div style="font-weight:700;color:#f97316">NF ' + escapeHtml(p.nf) + '</div>' + '<div style="font-weight:700;color:#f97316">NF ' + escapeHtml(p.nf) + '</div>' +
'<div style="font-weight:600;margin-bottom:4px">' + escapeHtml(p.nome) + '</div>' + '<div style="font-weight:600;margin-bottom:4px">' + escapeHtml(p.nome) + '</div>' +
statusLinha + statusLinha +
linha('📍', p.endereco) + linha(ico('geo-alt-fill'), p.endereco) +
linha('🏥', p.unidade) + linha(ico('hospital-fill'), p.unidade) +
linha('🚚', p.motorista) + linha(ico('truck'), p.motorista) +
linha('🚗', p.veiculo) + linha(ico('car-front-fill'), p.veiculo) +
linha('🕒', p.checkout) + linha(ico('clock-fill'), p.checkout) +
foto + foto +
'</div>'; '</div>';
} }
@@ -513,7 +519,9 @@
const mapa = L.map(el, { layers: [escuro] }).setView(centro, 11); const mapa = L.map(el, { layers: [escuro] }).setView(centro, 11);
L.control.layers( L.control.layers(
{ '🌙 Escuro': escuro, '🗺️ Claro': claro, '🛰️ Satélite': satelite }, { [ico('moon-fill') + ' Escuro']: escuro,
[ico('map-fill') + ' Claro']: claro,
[ico('broadcast-pin') + ' Satélite']: satelite },
{}, { position: 'topright', collapsed: true } {}, { position: 'topright', collapsed: true }
).addTo(mapa); ).addTo(mapa);

View File

@@ -15,10 +15,10 @@
<%# ── Cabeçalho ─────────────────────────────────────────── %> <%# ── Cabeçalho ─────────────────────────────────────────── %>
<div class="flex flex-col sm:flex-row sm:items-center justify-between gap-4"> <div class="flex flex-col sm:flex-row sm:items-center justify-between gap-4">
<div> <div>
<h1 class="text-2xl font-bold text-white">📋 Planilha da Operação</h1> <h1 class="text-2xl font-bold text-white flex items-center gap-2"><%= icone :planilha %> Planilha da Operação</h1>
<p class="text-gray-400 text-sm mt-0.5"> <p class="text-gray-400 text-sm mt-0.5">
<% if @modo == 'global' %> <% if @modo == 'global' %>
🌐 Global · <%= @periodo_inicio.strftime('%d/%m/%Y') %> <%= @periodo_fim.strftime('%d/%m/%Y') %> <%= icone :global %> Global · <%= @periodo_inicio.strftime('%d/%m/%Y') %> <%= @periodo_fim.strftime('%d/%m/%Y') %>
<% elsif @metricas&.data_inicio %> <% elsif @metricas&.data_inicio %>
<%= @metricas.operacoes_label %> · <%= @metricas.data_inicio.strftime('%d/%m/%Y') %> <%= @metricas.data_fim.strftime('%d/%m/%Y') %> <%= @metricas.operacoes_label %> · <%= @metricas.data_inicio.strftime('%d/%m/%Y') %> <%= @metricas.data_fim.strftime('%d/%m/%Y') %>
<% elsif @operacao %> <% elsif @operacao %>
@@ -27,7 +27,7 @@
</p> </p>
</div> </div>
<%= link_to 'Dashboard de Operações', <%= link_to rotulo(:voltar, 'Dashboard de Operações', cor: nil),
operacoes_dashboard_path({ modo: @modo, operacao: @operacao, inicio: ini_iso, fim: fim_iso }.compact), operacoes_dashboard_path({ modo: @modo, operacao: @operacao, inicio: ini_iso, fim: fim_iso }.compact),
data: { turbo: false }, data: { turbo: false },
class: 'px-4 py-2.5 rounded-xl text-sm whitespace-nowrap bg-[#1a1a1a] text-gray-300 border border-white/10 hover:border-orange-500' %> class: 'px-4 py-2.5 rounded-xl text-sm whitespace-nowrap bg-[#1a1a1a] text-gray-300 border border-white/10 hover:border-orange-500' %>
@@ -35,10 +35,10 @@
<%# ── Seleção de escopo: operação única ou global ───────── %> <%# ── Seleção de escopo: operação única ou global ───────── %>
<div class="flex flex-wrap items-center gap-2"> <div class="flex flex-wrap items-center gap-2">
<% pills = { 'operacao' => '🏥 Operação', 'global' => '🌐 Global' } %> <% pills = { 'operacao' => [:operacao, 'Operação'], 'global' => [:global, 'Global'] } %>
<% pills.each do |m, label| %> <% pills.each do |m, (ic, label)| %>
<% ativo = @modo == m %> <% ativo = @modo == m %>
<%= link_to label, <%= link_to rotulo(ic, label, cor: (ativo ? nil : 'text-brand-laranja')),
operacoes_planilha_path({ modo: m, operacao: @operacao, inicio: ini_iso, fim: fim_iso, q: @busca.presence }.compact), operacoes_planilha_path({ modo: m, operacao: @operacao, inicio: ini_iso, fim: fim_iso, q: @busca.presence }.compact),
data: { turbo: false }, data: { turbo: false },
class: "px-4 py-2 rounded-xl text-sm font-semibold whitespace-nowrap #{ativo ? 'bg-orange-500 text-black' : 'bg-[#1a1a1a] text-gray-300 border border-white/10 hover:border-orange-500'}" %> class: "px-4 py-2 rounded-xl text-sm font-semibold whitespace-nowrap #{ativo ? 'bg-orange-500 text-black' : 'bg-[#1a1a1a] text-gray-300 border border-white/10 hover:border-orange-500'}" %>
@@ -64,7 +64,7 @@
<%# Planilha Entregas preenchida (modelo entregue ao cliente) %> <%# Planilha Entregas preenchida (modelo entregue ao cliente) %>
<% if @operacao %> <% if @operacao %>
<%= link_to '⬇️ Baixar Excel preenchido', operacoes_planilha_baixar_path(operacao: @operacao), <%= link_to rotulo(:baixar, 'Baixar Excel preenchido', cor: nil), operacoes_planilha_baixar_path(operacao: @operacao),
data: { turbo: false }, data: { turbo: false },
title: 'Planilha Entregas da operação com status/ocorrências preenchidos pelo rastreio', title: 'Planilha Entregas da operação com status/ocorrências preenchidos pelo rastreio',
class: 'px-4 py-2.5 bg-orange-500 hover:bg-orange-600 text-black font-bold rounded-xl text-sm whitespace-nowrap' %> class: 'px-4 py-2.5 bg-orange-500 hover:bg-orange-600 text-black font-bold rounded-xl text-sm whitespace-nowrap' %>
@@ -79,7 +79,7 @@
<% @chips.each do |chip| %> <% @chips.each do |chip| %>
<span class="inline-flex items-center gap-2 px-3 py-1.5 rounded-full bg-orange-500/15 border border-orange-500/30 text-orange-300 text-xs"> <span class="inline-flex items-center gap-2 px-3 py-1.5 rounded-full bg-orange-500/15 border border-orange-500/30 text-orange-300 text-xs">
<%= chip[:rotulo] %>: <strong class="text-white"><%= chip[:display] || chip[:valor] %></strong> <%= chip[:rotulo] %>: <strong class="text-white"><%= chip[:display] || chip[:valor] %></strong>
<%= link_to '✕', operacoes_planilha_path(request.query_parameters.except(chip[:param], 'pg')), <%= link_to icone(:fechar, cor: nil, tamanho: 'w-3 h-3'), operacoes_planilha_path(request.query_parameters.except(chip[:param], 'pg')),
data: { turbo: false }, class: 'hover:text-white font-bold', title: 'Remover filtro' %> data: { turbo: false }, class: 'hover:text-white font-bold', title: 'Remover filtro' %>
</span> </span>
<% end %> <% end %>

View File

@@ -103,7 +103,7 @@
<% if @usuarios.empty? %> <% if @usuarios.empty? %>
<tr> <tr>
<td colspan="5" class="px-6 py-16 text-center text-gray-500"> <td colspan="5" class="px-6 py-16 text-center text-gray-500">
<div class="text-4xl mb-3">👥</div> <div class="mb-3"><%= icone :usuarios, tamanho: 'w-10 h-10', cor: 'text-gray-600' %></div>
<p>Nenhum usuário encontrado.</p> <p>Nenhum usuário encontrado.</p>
</td> </td>
</tr> </tr>

79
public/icons.svg Normal file
View File

@@ -0,0 +1,79 @@
<svg xmlns="http://www.w3.org/2000/svg" style="display:none">
<!-- Bootstrap Icons v1.x (MIT License) - subset for Reem Notas -->
<symbol class="bi bi-bar-chart-fill" viewBox="0 0 16 16" id="bar-chart-fill"><path d="M1 11a1 1 0 0 1 1-1h2a1 1 0 0 1 1 1v3a1 1 0 0 1-1 1H2a1 1 0 0 1-1-1zm5-4a1 1 0 0 1 1-1h2a1 1 0 0 1 1 1v7a1 1 0 0 1-1 1H7a1 1 0 0 1-1-1zm5-5a1 1 0 0 1 1-1h2a1 1 0 0 1 1 1v12a1 1 0 0 1-1 1h-2a1 1 0 0 1-1-1z"/></symbol>
<symbol class="bi bi-graph-up-arrow" viewBox="0 0 16 16" id="graph-up-arrow"><path fill-rule="evenodd" d="M0 0h1v15h15v1H0zm10 3.5a.5.5 0 0 1 .5-.5h4a.5.5 0 0 1 .5.5v4a.5.5 0 0 1-1 0V4.9l-3.613 4.417a.5.5 0 0 1-.74.037L7.06 6.767l-3.656 5.027a.5.5 0 0 1-.808-.588l4-5.5a.5.5 0 0 1 .758-.06l2.609 2.61L13.445 4H10.5a.5.5 0 0 1-.5-.5"/></symbol>
<symbol class="bi bi-box-seam-fill" viewBox="0 0 16 16" id="box-seam-fill"><path fill-rule="evenodd" d="M15.528 2.973a.75.75 0 0 1 .472.696v8.662a.75.75 0 0 1-.472.696l-7.25 2.9a.75.75 0 0 1-.557 0l-7.25-2.9A.75.75 0 0 1 0 12.331V3.669a.75.75 0 0 1 .471-.696L7.443.184l.01-.003.268-.108a.75.75 0 0 1 .558 0l.269.108.01.003zM10.404 2 4.25 4.461 1.846 3.5 1 3.839v.4l6.5 2.6v7.922l.5.2.5-.2V6.84l6.5-2.6v-.4l-.846-.339L8 5.961 5.596 5l6.154-2.461z"/></symbol>
<symbol class="bi bi-folder-fill" viewBox="0 0 16 16" id="folder-fill"><path d="M9.828 3h3.982a2 2 0 0 1 1.992 2.181l-.637 7A2 2 0 0 1 13.174 14H2.825a2 2 0 0 1-1.991-1.819l-.637-7a2 2 0 0 1 .342-1.31L.5 3a2 2 0 0 1 2-2h3.672a2 2 0 0 1 1.414.586l.828.828A2 2 0 0 0 9.828 3m-8.322.12q.322-.119.684-.12h5.396l-.707-.707A1 1 0 0 0 6.172 2H2.5a1 1 0 0 0-1 .981z"/></symbol>
<symbol class="bi bi-house-fill" viewBox="0 0 16 16" id="house-fill"><path d="M8.707 1.5a1 1 0 0 0-1.414 0L.646 8.146a.5.5 0 0 0 .708.708L8 2.207l6.646 6.647a.5.5 0 0 0 .708-.708L13 5.793V2.5a.5.5 0 0 0-.5-.5h-1a.5.5 0 0 0-.5.5v1.293z"/><path d="m8 3.293 6 6V13.5a1.5 1.5 0 0 1-1.5 1.5h-9A1.5 1.5 0 0 1 2 13.5V9.293z"/></symbol>
<symbol class="bi bi-people-fill" viewBox="0 0 16 16" id="people-fill"><path d="M7 14s-1 0-1-1 1-4 5-4 5 3 5 4-1 1-1 1zm4-6a3 3 0 1 0 0-6 3 3 0 0 0 0 6m-5.784 6A2.24 2.24 0 0 1 5 13c0-1.355.68-2.75 1.936-3.72A6.3 6.3 0 0 0 5 9c-4 0-5 3-5 4s1 1 1 1zM4.5 8a2.5 2.5 0 1 0 0-5 2.5 2.5 0 0 0 0 5"/></symbol>
<symbol class="bi bi-gear-fill" viewBox="0 0 16 16" id="gear-fill"><path d="M9.405 1.05c-.413-1.4-2.397-1.4-2.81 0l-.1.34a1.464 1.464 0 0 1-2.105.872l-.31-.17c-1.283-.698-2.686.705-1.987 1.987l.169.311c.446.82.023 1.841-.872 2.105l-.34.1c-1.4.413-1.4 2.397 0 2.81l.34.1a1.464 1.464 0 0 1 .872 2.105l-.17.31c-.698 1.283.705 2.686 1.987 1.987l.311-.169a1.464 1.464 0 0 1 2.105.872l.1.34c.413 1.4 2.397 1.4 2.81 0l.1-.34a1.464 1.464 0 0 1 2.105-.872l.31.17c1.283.698 2.686-.705 1.987-1.987l-.169-.311a1.464 1.464 0 0 1 .872-2.105l.34-.1c1.4-.413 1.4-2.397 0-2.81l-.34-.1a1.464 1.464 0 0 1-.872-2.105l.17-.31c.698-1.283-.705-2.686-1.987-1.987l-.311.169a1.464 1.464 0 0 1-2.105-.872zM8 10.93a2.929 2.929 0 1 1 0-5.86 2.929 2.929 0 0 1 0 5.858z"/></symbol>
<symbol class="bi bi-search" viewBox="0 0 16 16" id="search"><path d="M11.742 10.344a6.5 6.5 0 1 0-1.397 1.398h-.001q.044.06.098.115l3.85 3.85a1 1 0 0 0 1.415-1.414l-3.85-3.85a1 1 0 0 0-.115-.1zM12 6.5a5.5 5.5 0 1 1-11 0 5.5 5.5 0 0 1 11 0"/></symbol>
<symbol class="bi bi-box-arrow-in-down" viewBox="0 0 16 16" id="box-arrow-in-down"><path fill-rule="evenodd" d="M3.5 6a.5.5 0 0 0-.5.5v8a.5.5 0 0 0 .5.5h9a.5.5 0 0 0 .5-.5v-8a.5.5 0 0 0-.5-.5h-2a.5.5 0 0 1 0-1h2A1.5 1.5 0 0 1 14 6.5v8a1.5 1.5 0 0 1-1.5 1.5h-9A1.5 1.5 0 0 1 2 14.5v-8A1.5 1.5 0 0 1 3.5 5h2a.5.5 0 0 1 0 1z"/><path fill-rule="evenodd" d="M7.646 11.854a.5.5 0 0 0 .708 0l3-3a.5.5 0 0 0-.708-.708L8.5 10.293V1.5a.5.5 0 0 0-1 0v8.793L5.354 8.146a.5.5 0 1 0-.708.708z"/></symbol>
<symbol class="bi bi-pencil-fill" viewBox="0 0 16 16" id="pencil-fill"><path d="M12.854.146a.5.5 0 0 0-.707 0L10.5 1.793 14.207 5.5l1.647-1.646a.5.5 0 0 0 0-.708zm.646 6.061L9.793 2.5 3.293 9H3.5a.5.5 0 0 1 .5.5v.5h.5a.5.5 0 0 1 .5.5v.5h.5a.5.5 0 0 1 .5.5v.5h.5a.5.5 0 0 1 .5.5v.207zm-7.468 7.468A.5.5 0 0 1 6 13.5V13h-.5a.5.5 0 0 1-.5-.5V12h-.5a.5.5 0 0 1-.5-.5V11h-.5a.5.5 0 0 1-.5-.5V10h-.5a.5.5 0 0 1-.175-.032l-.179.178a.5.5 0 0 0-.11.168l-2 5a.5.5 0 0 0 .65.65l5-2a.5.5 0 0 0 .168-.11z"/></symbol>
<symbol class="bi bi-box-arrow-right" viewBox="0 0 16 16" id="box-arrow-right"><path fill-rule="evenodd" d="M10 12.5a.5.5 0 0 1-.5.5h-8a.5.5 0 0 1-.5-.5v-9a.5.5 0 0 1 .5-.5h8a.5.5 0 0 1 .5.5v2a.5.5 0 0 0 1 0v-2A1.5 1.5 0 0 0 9.5 2h-8A1.5 1.5 0 0 0 0 3.5v9A1.5 1.5 0 0 0 1.5 14h8a1.5 1.5 0 0 0 1.5-1.5v-2a.5.5 0 0 0-1 0z"/><path fill-rule="evenodd" d="M15.854 8.354a.5.5 0 0 0 0-.708l-3-3a.5.5 0 0 0-.708.708L14.293 7.5H5.5a.5.5 0 0 0 0 1h8.793l-2.147 2.146a.5.5 0 0 0 .708.708z"/></symbol>
<symbol class="bi bi-list" viewBox="0 0 16 16" id="list"><path fill-rule="evenodd" d="M2.5 12a.5.5 0 0 1 .5-.5h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5m0-4a.5.5 0 0 1 .5-.5h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5m0-4a.5.5 0 0 1 .5-.5h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5"/></symbol>
<symbol class="bi bi-chevron-left" viewBox="0 0 16 16" id="chevron-left"><path fill-rule="evenodd" d="M11.354 1.646a.5.5 0 0 1 0 .708L5.707 8l5.647 5.646a.5.5 0 0 1-.708.708l-6-6a.5.5 0 0 1 0-.708l6-6a.5.5 0 0 1 .708 0"/></symbol>
<symbol class="bi bi-chevron-down" viewBox="0 0 16 16" id="chevron-down"><path fill-rule="evenodd" d="M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708"/></symbol>
<symbol class="bi bi-clipboard-data-fill" viewBox="0 0 16 16" id="clipboard-data-fill"><path d="M6.5 0A1.5 1.5 0 0 0 5 1.5v1A1.5 1.5 0 0 0 6.5 4h3A1.5 1.5 0 0 0 11 2.5v-1A1.5 1.5 0 0 0 9.5 0zm3 1a.5.5 0 0 1 .5.5v1a.5.5 0 0 1-.5.5h-3a.5.5 0 0 1-.5-.5v-1a.5.5 0 0 1 .5-.5z"/><path d="M4 1.5H3a2 2 0 0 0-2 2V14a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V3.5a2 2 0 0 0-2-2h-1v1A2.5 2.5 0 0 1 9.5 5h-3A2.5 2.5 0 0 1 4 2.5zM10 8a1 1 0 1 1 2 0v5a1 1 0 1 1-2 0zm-6 4a1 1 0 1 1 2 0v1a1 1 0 1 1-2 0zm4-3a1 1 0 0 1 1 1v3a1 1 0 1 1-2 0v-3a1 1 0 0 1 1-1"/></symbol>
<symbol class="bi bi-truck" viewBox="0 0 16 16" id="truck"><path d="M0 3.5A1.5 1.5 0 0 1 1.5 2h9A1.5 1.5 0 0 1 12 3.5V5h1.02a1.5 1.5 0 0 1 1.17.563l1.481 1.85a1.5 1.5 0 0 1 .329.938V10.5a1.5 1.5 0 0 1-1.5 1.5H14a2 2 0 1 1-4 0H5a2 2 0 1 1-3.998-.085A1.5 1.5 0 0 1 0 10.5zm1.294 7.456A2 2 0 0 1 4.732 11h5.536a2 2 0 0 1 .732-.732V3.5a.5.5 0 0 0-.5-.5h-9a.5.5 0 0 0-.5.5v7a.5.5 0 0 0 .294.456M12 10a2 2 0 0 1 1.732 1h.768a.5.5 0 0 0 .5-.5V8.35a.5.5 0 0 0-.11-.312l-1.48-1.85A.5.5 0 0 0 13.02 6H12zm-9 1a1 1 0 1 0 0 2 1 1 0 0 0 0-2m9 0a1 1 0 1 0 0 2 1 1 0 0 0 0-2"/></symbol>
<symbol class="bi bi-car-front-fill" viewBox="0 0 16 16" id="car-front-fill"><path d="M2.52 3.515A2.5 2.5 0 0 1 4.82 2h6.362c1 0 1.904.596 2.298 1.515l.792 1.848c.075.175.21.319.38.404.5.25.855.715.965 1.262l.335 1.679q.05.242.049.49v.413c0 .814-.39 1.543-1 1.997V13.5a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1-.5-.5v-1.338c-1.292.048-2.745.088-4 .088s-2.708-.04-4-.088V13.5a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1-.5-.5v-1.892c-.61-.454-1-1.183-1-1.997v-.413a2.5 2.5 0 0 1 .049-.49l.335-1.68c.11-.546.465-1.012.964-1.261a.8.8 0 0 0 .381-.404l.792-1.848ZM3 10a1 1 0 1 0 0-2 1 1 0 0 0 0 2m10 0a1 1 0 1 0 0-2 1 1 0 0 0 0 2M6 8a1 1 0 0 0 0 2h4a1 1 0 1 0 0-2zM2.906 5.189a.51.51 0 0 0 .497.731c.91-.073 3.35-.17 4.597-.17s3.688.097 4.597.17a.51.51 0 0 0 .497-.731l-.956-1.913A.5.5 0 0 0 11.691 3H4.309a.5.5 0 0 0-.447.276L2.906 5.19Z"/></symbol>
<symbol class="bi bi-cash-coin" viewBox="0 0 16 16" id="cash-coin"><path fill-rule="evenodd" d="M11 15a4 4 0 1 0 0-8 4 4 0 0 0 0 8m5-4a5 5 0 1 1-10 0 5 5 0 0 1 10 0"/><path d="M9.438 11.944c.047.596.518 1.06 1.363 1.116v.44h.375v-.443c.875-.061 1.386-.529 1.386-1.207 0-.618-.39-.936-1.09-1.1l-.296-.07v-1.2c.376.043.614.248.671.532h.658c-.047-.575-.54-1.024-1.329-1.073V8.5h-.375v.45c-.747.073-1.255.522-1.255 1.158 0 .562.378.92 1.007 1.066l.248.061v1.272c-.384-.058-.639-.27-.696-.563h-.668zm1.36-1.354c-.369-.085-.569-.26-.569-.522 0-.294.216-.514.572-.578v1.1zm.432.746c.449.104.655.272.655.569 0 .339-.257.571-.709.614v-1.195z"/><path d="M1 0a1 1 0 0 0-1 1v8a1 1 0 0 0 1 1h4.083q.088-.517.258-1H3a2 2 0 0 0-2-2V3a2 2 0 0 0 2-2h10a2 2 0 0 0 2 2v3.528c.38.34.717.728 1 1.154V1a1 1 0 0 0-1-1z"/><path d="M9.998 5.083 10 5a2 2 0 1 0-3.132 1.65 6 6 0 0 1 3.13-1.567"/></symbol>
<symbol class="bi bi-cash-stack" viewBox="0 0 16 16" id="cash-stack"><path d="M1 3a1 1 0 0 1 1-1h12a1 1 0 0 1 1 1zm7 8a2 2 0 1 0 0-4 2 2 0 0 0 0 4"/><path d="M0 5a1 1 0 0 1 1-1h14a1 1 0 0 1 1 1v8a1 1 0 0 1-1 1H1a1 1 0 0 1-1-1zm3 0a2 2 0 0 1-2 2v4a2 2 0 0 1 2 2h10a2 2 0 0 1 2-2V7a2 2 0 0 1-2-2z"/></symbol>
<symbol class="bi bi-cash" viewBox="0 0 16 16" id="cash"><path d="M8 10a2 2 0 1 0 0-4 2 2 0 0 0 0 4"/><path d="M0 4a1 1 0 0 1 1-1h14a1 1 0 0 1 1 1v8a1 1 0 0 1-1 1H1a1 1 0 0 1-1-1zm3 0a2 2 0 0 1-2 2v4a2 2 0 0 1 2 2h10a2 2 0 0 1 2-2V6a2 2 0 0 1-2-2z"/></symbol>
<symbol class="bi bi-briefcase-fill" viewBox="0 0 16 16" id="briefcase-fill"><path d="M6.5 1A1.5 1.5 0 0 0 5 2.5V3H1.5A1.5 1.5 0 0 0 0 4.5v1.384l7.614 2.03a1.5 1.5 0 0 0 .772 0L16 5.884V4.5A1.5 1.5 0 0 0 14.5 3H11v-.5A1.5 1.5 0 0 0 9.5 1zm0 1h3a.5.5 0 0 1 .5.5V3H6v-.5a.5.5 0 0 1 .5-.5"/><path d="M0 12.5A1.5 1.5 0 0 0 1.5 14h13a1.5 1.5 0 0 0 1.5-1.5V6.85L8.129 8.947a.5.5 0 0 1-.258 0L0 6.85z"/></symbol>
<symbol class="bi bi-calendar-event" viewBox="0 0 16 16" id="calendar-event"><path d="M11 6.5a.5.5 0 0 1 .5-.5h1a.5.5 0 0 1 .5.5v1a.5.5 0 0 1-.5.5h-1a.5.5 0 0 1-.5-.5z"/><path d="M3.5 0a.5.5 0 0 1 .5.5V1h8V.5a.5.5 0 0 1 1 0V1h1a2 2 0 0 1 2 2v11a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V3a2 2 0 0 1 2-2h1V.5a.5.5 0 0 1 .5-.5M1 4v10a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1V4z"/></symbol>
<symbol class="bi bi-receipt" viewBox="0 0 16 16" id="receipt"><path d="M1.92.506a.5.5 0 0 1 .434.14L3 1.293l.646-.647a.5.5 0 0 1 .708 0L5 1.293l.646-.647a.5.5 0 0 1 .708 0L7 1.293l.646-.647a.5.5 0 0 1 .708 0L9 1.293l.646-.647a.5.5 0 0 1 .708 0l.646.647.646-.647a.5.5 0 0 1 .708 0l.646.647.646-.647a.5.5 0 0 1 .801.13l.5 1A.5.5 0 0 1 15 2v12a.5.5 0 0 1-.053.224l-.5 1a.5.5 0 0 1-.8.13L13 14.707l-.646.647a.5.5 0 0 1-.708 0L11 14.707l-.646.647a.5.5 0 0 1-.708 0L9 14.707l-.646.647a.5.5 0 0 1-.708 0L7 14.707l-.646.647a.5.5 0 0 1-.708 0L5 14.707l-.646.647a.5.5 0 0 1-.708 0L3 14.707l-.646.647a.5.5 0 0 1-.801-.13l-.5-1A.5.5 0 0 1 1 14V2a.5.5 0 0 1 .053-.224l.5-1a.5.5 0 0 1 .367-.27m.217 1.338L2 2.118v11.764l.137.274.51-.51a.5.5 0 0 1 .707 0l.646.647.646-.646a.5.5 0 0 1 .708 0l.646.646.646-.646a.5.5 0 0 1 .708 0l.646.646.646-.646a.5.5 0 0 1 .708 0l.646.646.646-.646a.5.5 0 0 1 .708 0l.646.646.646-.646a.5.5 0 0 1 .708 0l.509.509.137-.274V2.118l-.137-.274-.51.51a.5.5 0 0 1-.707 0L12 1.707l-.646.647a.5.5 0 0 1-.708 0L10 1.707l-.646.647a.5.5 0 0 1-.708 0L8 1.707l-.646.647a.5.5 0 0 1-.708 0L6 1.707l-.646.647a.5.5 0 0 1-.708 0L4 1.707l-.646.647a.5.5 0 0 1-.708 0z"/><path d="M3 4.5a.5.5 0 0 1 .5-.5h6a.5.5 0 1 1 0 1h-6a.5.5 0 0 1-.5-.5m0 2a.5.5 0 0 1 .5-.5h6a.5.5 0 1 1 0 1h-6a.5.5 0 0 1-.5-.5m0 2a.5.5 0 0 1 .5-.5h6a.5.5 0 1 1 0 1h-6a.5.5 0 0 1-.5-.5m0 2a.5.5 0 0 1 .5-.5h6a.5.5 0 0 1 0 1h-6a.5.5 0 0 1-.5-.5m8-6a.5.5 0 0 1 .5-.5h1a.5.5 0 0 1 0 1h-1a.5.5 0 0 1-.5-.5m0 2a.5.5 0 0 1 .5-.5h1a.5.5 0 0 1 0 1h-1a.5.5 0 0 1-.5-.5m0 2a.5.5 0 0 1 .5-.5h1a.5.5 0 0 1 0 1h-1a.5.5 0 0 1-.5-.5m0 2a.5.5 0 0 1 .5-.5h1a.5.5 0 0 1 0 1h-1a.5.5 0 0 1-.5-.5"/></symbol>
<symbol class="bi bi-file-earmark-text-fill" viewBox="0 0 16 16" id="file-earmark-text-fill"><path d="M9.293 0H4a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h8a2 2 0 0 0 2-2V4.707A1 1 0 0 0 13.707 4L10 .293A1 1 0 0 0 9.293 0M9.5 3.5v-2l3 3h-2a1 1 0 0 1-1-1M4.5 9a.5.5 0 0 1 0-1h7a.5.5 0 0 1 0 1zM4 10.5a.5.5 0 0 1 .5-.5h7a.5.5 0 0 1 0 1h-7a.5.5 0 0 1-.5-.5m.5 2.5a.5.5 0 0 1 0-1h4a.5.5 0 0 1 0 1z"/></symbol>
<symbol class="bi bi-pencil-square" viewBox="0 0 16 16" id="pencil-square"><path d="M15.502 1.94a.5.5 0 0 1 0 .706L14.459 3.69l-2-2L13.502.646a.5.5 0 0 1 .707 0l1.293 1.293zm-1.75 2.456-2-2L4.939 9.21a.5.5 0 0 0-.121.196l-.805 2.414a.25.25 0 0 0 .316.316l2.414-.805a.5.5 0 0 0 .196-.12l6.813-6.814z"/><path fill-rule="evenodd" d="M1 13.5A1.5 1.5 0 0 0 2.5 15h11a1.5 1.5 0 0 0 1.5-1.5v-6a.5.5 0 0 0-1 0v6a.5.5 0 0 1-.5.5h-11a.5.5 0 0 1-.5-.5v-11a.5.5 0 0 1 .5-.5H9a.5.5 0 0 0 0-1H2.5A1.5 1.5 0 0 0 1 2.5z"/></symbol>
<symbol class="bi bi-geo-alt-fill" viewBox="0 0 16 16" id="geo-alt-fill"><path d="M8 16s6-5.686 6-10A6 6 0 0 0 2 6c0 4.314 6 10 6 10m0-7a3 3 0 1 1 0-6 3 3 0 0 1 0 6"/></symbol>
<symbol class="bi bi-hospital-fill" viewBox="0 0 16 16" id="hospital-fill"><path d="M6 0a1 1 0 0 0-1 1v1a1 1 0 0 0-1 1v4H1a1 1 0 0 0-1 1v7a1 1 0 0 0 1 1h6v-2.5a.5.5 0 0 1 .5-.5h1a.5.5 0 0 1 .5.5V16h6a1 1 0 0 0 1-1V8a1 1 0 0 0-1-1h-3V3a1 1 0 0 0-1-1V1a1 1 0 0 0-1-1zm2.5 5.034v1.1l.953-.55.5.867L9 7l.953.55-.5.866-.953-.55v1.1h-1v-1.1l-.953.55-.5-.866L7 7l-.953-.55.5-.866.953.55v-1.1zM2.25 9h.5a.25.25 0 0 1 .25.25v.5a.25.25 0 0 1-.25.25h-.5A.25.25 0 0 1 2 9.75v-.5A.25.25 0 0 1 2.25 9m0 2h.5a.25.25 0 0 1 .25.25v.5a.25.25 0 0 1-.25.25h-.5a.25.25 0 0 1-.25-.25v-.5a.25.25 0 0 1 .25-.25M2 13.25a.25.25 0 0 1 .25-.25h.5a.25.25 0 0 1 .25.25v.5a.25.25 0 0 1-.25.25h-.5a.25.25 0 0 1-.25-.25zM13.25 9h.5a.25.25 0 0 1 .25.25v.5a.25.25 0 0 1-.25.25h-.5a.25.25 0 0 1-.25-.25v-.5a.25.25 0 0 1 .25-.25M13 11.25a.25.25 0 0 1 .25-.25h.5a.25.25 0 0 1 .25.25v.5a.25.25 0 0 1-.25.25h-.5a.25.25 0 0 1-.25-.25zm.25 1.75h.5a.25.25 0 0 1 .25.25v.5a.25.25 0 0 1-.25.25h-.5a.25.25 0 0 1-.25-.25v-.5a.25.25 0 0 1 .25-.25"/></symbol>
<symbol class="bi bi-globe2" viewBox="0 0 16 16" id="globe2"><path d="M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8m7.5-6.923c-.67.204-1.335.82-1.887 1.855q-.215.403-.395.872c.705.157 1.472.257 2.282.287zM4.249 3.539q.214-.577.481-1.078a7 7 0 0 1 .597-.933A7 7 0 0 0 3.051 3.05q.544.277 1.198.49zM3.509 7.5c.036-1.07.188-2.087.436-3.008a9 9 0 0 1-1.565-.667A6.96 6.96 0 0 0 1.018 7.5zm1.4-2.741a12.3 12.3 0 0 0-.4 2.741H7.5V5.091c-.91-.03-1.783-.145-2.591-.332M8.5 5.09V7.5h2.99a12.3 12.3 0 0 0-.399-2.741c-.808.187-1.681.301-2.591.332zM4.51 8.5c.035.987.176 1.914.399 2.741A13.6 13.6 0 0 1 7.5 10.91V8.5zm3.99 0v2.409c.91.03 1.783.145 2.591.332.223-.827.364-1.754.4-2.741zm-3.282 3.696q.18.469.395.872c.552 1.035 1.218 1.65 1.887 1.855V11.91c-.81.03-1.577.13-2.282.287zm.11 2.276a7 7 0 0 1-.598-.933 9 9 0 0 1-.481-1.079 8.4 8.4 0 0 0-1.198.49 7 7 0 0 0 2.276 1.522zm-1.383-2.964A13.4 13.4 0 0 1 3.508 8.5h-2.49a6.96 6.96 0 0 0 1.362 3.675c.47-.258.995-.482 1.565-.667m6.728 2.964a7 7 0 0 0 2.275-1.521 8.4 8.4 0 0 0-1.197-.49 9 9 0 0 1-.481 1.078 7 7 0 0 1-.597.933M8.5 11.909v3.014c.67-.204 1.335-.82 1.887-1.855q.216-.403.395-.872A12.6 12.6 0 0 0 8.5 11.91zm3.555-.401c.57.185 1.095.409 1.565.667A6.96 6.96 0 0 0 14.982 8.5h-2.49a13.4 13.4 0 0 1-.437 3.008M14.982 7.5a6.96 6.96 0 0 0-1.362-3.675c-.47.258-.995.482-1.565.667.248.92.4 1.938.437 3.008zM11.27 2.461q.266.502.482 1.078a8.4 8.4 0 0 0 1.196-.49 7 7 0 0 0-2.275-1.52c.218.283.418.597.597.932m-.488 1.343a8 8 0 0 0-.395-.872C9.835 1.897 9.17 1.282 8.5 1.077V4.09c.81-.03 1.577-.13 2.282-.287z"/></symbol>
<symbol class="bi bi-arrow-left-right" viewBox="0 0 16 16" id="arrow-left-right"><path fill-rule="evenodd" d="M1 11.5a.5.5 0 0 0 .5.5h11.793l-3.147 3.146a.5.5 0 0 0 .708.708l4-4a.5.5 0 0 0 0-.708l-4-4a.5.5 0 0 0-.708.708L13.293 11H1.5a.5.5 0 0 0-.5.5m14-7a.5.5 0 0 1-.5.5H2.707l3.147 3.146a.5.5 0 1 1-.708.708l-4-4a.5.5 0 0 1 0-.708l4-4a.5.5 0 1 1 .708.708L2.707 4H14.5a.5.5 0 0 1 .5.5"/></symbol>
<symbol class="bi bi-plus-lg" viewBox="0 0 16 16" id="plus-lg"><path fill-rule="evenodd" d="M8 2a.5.5 0 0 1 .5.5v5h5a.5.5 0 0 1 0 1h-5v5a.5.5 0 0 1-1 0v-5h-5a.5.5 0 0 1 0-1h5v-5A.5.5 0 0 1 8 2"/></symbol>
<symbol class="bi bi-x-lg" viewBox="0 0 16 16" id="x-lg"><path d="M2.146 2.854a.5.5 0 1 1 .708-.708L8 7.293l5.146-5.147a.5.5 0 0 1 .708.708L8.707 8l5.147 5.146a.5.5 0 0 1-.708.708L8 8.707l-5.146 5.147a.5.5 0 0 1-.708-.708L7.293 8z"/></symbol>
<symbol class="bi bi-check-lg" viewBox="0 0 16 16" id="check-lg"><path d="M12.736 3.97a.733.733 0 0 1 1.047 0c.286.289.29.756.01 1.05L7.88 12.01a.733.733 0 0 1-1.065.02L3.217 8.384a.757.757 0 0 1 0-1.06.733.733 0 0 1 1.047 0l3.052 3.093 5.4-6.425z"/></symbol>
<symbol class="bi bi-lock-fill" viewBox="0 0 16 16" id="lock-fill"><path fill-rule="evenodd" d="M8 0a4 4 0 0 1 4 4v2.05a2.5 2.5 0 0 1 2 2.45v5a2.5 2.5 0 0 1-2.5 2.5h-7A2.5 2.5 0 0 1 2 13.5v-5a2.5 2.5 0 0 1 2-2.45V4a4 4 0 0 1 4-4m0 1a3 3 0 0 0-3 3v2h6V4a3 3 0 0 0-3-3"/></symbol>
<symbol class="bi bi-unlock-fill" viewBox="0 0 16 16" id="unlock-fill"><path fill-rule="evenodd" d="M12 0a4 4 0 0 1 4 4v2.5h-1V4a3 3 0 1 0-6 0v2h.5A2.5 2.5 0 0 1 12 8.5v5A2.5 2.5 0 0 1 9.5 16h-7A2.5 2.5 0 0 1 0 13.5v-5A2.5 2.5 0 0 1 2.5 6H8V4a4 4 0 0 1 4-4"/></symbol>
<symbol class="bi bi-archive-fill" viewBox="0 0 16 16" id="archive-fill"><path d="M12.643 15C13.979 15 15 13.845 15 12.5V5H1v7.5C1 13.845 2.021 15 3.357 15zM5.5 7h5a.5.5 0 0 1 0 1h-5a.5.5 0 0 1 0-1M.8 1a.8.8 0 0 0-.8.8V3a.8.8 0 0 0 .8.8h14.4A.8.8 0 0 0 16 3V1.8a.8.8 0 0 0-.8-.8z"/></symbol>
<symbol class="bi bi-folder2-open" viewBox="0 0 16 16" id="folder2-open"><path d="M1 3.5A1.5 1.5 0 0 1 2.5 2h2.764c.958 0 1.76.56 2.311 1.184C7.985 3.648 8.48 4 9 4h4.5A1.5 1.5 0 0 1 15 5.5v.64c.57.265.94.876.856 1.546l-.64 5.124A2.5 2.5 0 0 1 12.733 15H3.266a2.5 2.5 0 0 1-2.481-2.19l-.64-5.124A1.5 1.5 0 0 1 1 6.14zM2 6h12v-.5a.5.5 0 0 0-.5-.5H9c-.964 0-1.71-.629-2.174-1.154C6.374 3.334 5.82 3 5.264 3H2.5a.5.5 0 0 0-.5.5zm-.367 1a.5.5 0 0 0-.496.562l.64 5.124A1.5 1.5 0 0 0 3.266 14h9.468a1.5 1.5 0 0 0 1.489-1.314l.64-5.124A.5.5 0 0 0 14.367 7z"/></symbol>
<symbol class="bi bi-trash-fill" viewBox="0 0 16 16" id="trash-fill"><path d="M2.5 1a1 1 0 0 0-1 1v1a1 1 0 0 0 1 1H3v9a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2V4h.5a1 1 0 0 0 1-1V2a1 1 0 0 0-1-1H10a1 1 0 0 0-1-1H7a1 1 0 0 0-1 1zm3 4a.5.5 0 0 1 .5.5v7a.5.5 0 0 1-1 0v-7a.5.5 0 0 1 .5-.5M8 5a.5.5 0 0 1 .5.5v7a.5.5 0 0 1-1 0v-7A.5.5 0 0 1 8 5m3 .5v7a.5.5 0 0 1-1 0v-7a.5.5 0 0 1 1 0"/></symbol>
<symbol class="bi bi-arrow-clockwise" viewBox="0 0 16 16" id="arrow-clockwise"><path fill-rule="evenodd" d="M8 3a5 5 0 1 0 4.546 2.914.5.5 0 0 1 .908-.417A6 6 0 1 1 8 2z"/><path d="M8 4.466V.534a.25.25 0 0 1 .41-.192l2.36 1.966c.12.1.12.284 0 .384L8.41 4.658A.25.25 0 0 1 8 4.466"/></symbol>
<symbol class="bi bi-floppy-fill" viewBox="0 0 16 16" id="floppy-fill"><path d="M0 1.5A1.5 1.5 0 0 1 1.5 0H3v5.5A1.5 1.5 0 0 0 4.5 7h7A1.5 1.5 0 0 0 13 5.5V0h.086a1.5 1.5 0 0 1 1.06.44l1.415 1.414A1.5 1.5 0 0 1 16 2.914V14.5a1.5 1.5 0 0 1-1.5 1.5H14v-5.5A1.5 1.5 0 0 0 12.5 9h-9A1.5 1.5 0 0 0 2 10.5V16h-.5A1.5 1.5 0 0 1 0 14.5z"/><path d="M3 16h10v-5.5a.5.5 0 0 0-.5-.5h-9a.5.5 0 0 0-.5.5zm9-16H4v5.5a.5.5 0 0 0 .5.5h7a.5.5 0 0 0 .5-.5zM9 1h2v4H9z"/></symbol>
<symbol class="bi bi-printer-fill" viewBox="0 0 16 16" id="printer-fill"><path d="M5 1a2 2 0 0 0-2 2v1h10V3a2 2 0 0 0-2-2zm6 8H5a1 1 0 0 0-1 1v3a1 1 0 0 0 1 1h6a1 1 0 0 0 1-1v-3a1 1 0 0 0-1-1"/><path d="M0 7a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v3a2 2 0 0 1-2 2h-1v-2a2 2 0 0 0-2-2H5a2 2 0 0 0-2 2v2H2a2 2 0 0 1-2-2zm2.5 1a.5.5 0 1 0 0-1 .5.5 0 0 0 0 1"/></symbol>
<symbol class="bi bi-eye-fill" viewBox="0 0 16 16" id="eye-fill"><path d="M10.5 8a2.5 2.5 0 1 1-5 0 2.5 2.5 0 0 1 5 0"/><path d="M0 8s3-5.5 8-5.5S16 8 16 8s-3 5.5-8 5.5S0 8 0 8m8 3.5a3.5 3.5 0 1 0 0-7 3.5 3.5 0 0 0 0 7"/></symbol>
<symbol class="bi bi-person-fill" viewBox="0 0 16 16" id="person-fill"><path d="M3 14s-1 0-1-1 1-4 6-4 6 3 6 4-1 1-1 1zm5-6a3 3 0 1 0 0-6 3 3 0 0 0 0 6"/></symbol>
<symbol class="bi bi-person-badge-fill" viewBox="0 0 16 16" id="person-badge-fill"><path d="M2 2a2 2 0 0 1 2-2h8a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2zm4.5 0a.5.5 0 0 0 0 1h3a.5.5 0 0 0 0-1zM8 11a3 3 0 1 0 0-6 3 3 0 0 0 0 6m5 2.755C12.146 12.825 10.623 12 8 12s-4.146.826-5 1.755V14a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1z"/></symbol>
<symbol class="bi bi-key-fill" viewBox="0 0 16 16" id="key-fill"><path d="M3.5 11.5a3.5 3.5 0 1 1 3.163-5H14L15.5 8 14 9.5l-1-1-1 1-1-1-1 1-1-1-1 1H6.663a3.5 3.5 0 0 1-3.163 2M2.5 9a1 1 0 1 0 0-2 1 1 0 0 0 0 2"/></symbol>
<symbol class="bi bi-envelope-fill" viewBox="0 0 16 16" id="envelope-fill"><path d="M.05 3.555A2 2 0 0 1 2 2h12a2 2 0 0 1 1.95 1.555L8 8.414zM0 4.697v7.104l5.803-3.558zM6.761 8.83l-6.57 4.027A2 2 0 0 0 2 14h12a2 2 0 0 0 1.808-1.144l-6.57-4.027L8 9.586zm3.436-.586L16 11.801V4.697z"/></symbol>
<symbol class="bi bi-telephone-fill" viewBox="0 0 16 16" id="telephone-fill"><path fill-rule="evenodd" d="M1.885.511a1.745 1.745 0 0 1 2.61.163L6.29 2.98c.329.423.445.974.315 1.494l-.547 2.19a.68.68 0 0 0 .178.643l2.457 2.457a.68.68 0 0 0 .644.178l2.189-.547a1.75 1.75 0 0 1 1.494.315l2.306 1.794c.829.645.905 1.87.163 2.611l-1.034 1.034c-.74.74-1.846 1.065-2.877.702a18.6 18.6 0 0 1-7.01-4.42 18.6 18.6 0 0 1-4.42-7.009c-.362-1.03-.037-2.137.703-2.877z"/></symbol>
<symbol class="bi bi-clock-fill" viewBox="0 0 16 16" id="clock-fill"><path d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0M8 3.5a.5.5 0 0 0-1 0V9a.5.5 0 0 0 .252.434l3.5 2a.5.5 0 0 0 .496-.868L8 8.71z"/></symbol>
<symbol class="bi bi-clock-history" viewBox="0 0 16 16" id="clock-history"><path d="M8.515 1.019A7 7 0 0 0 8 1V0a8 8 0 0 1 .589.022zm2.004.45a7 7 0 0 0-.985-.299l.219-.976q.576.129 1.126.342zm1.37.71a7 7 0 0 0-.439-.27l.493-.87a8 8 0 0 1 .979.654l-.615.789a7 7 0 0 0-.418-.302zm1.834 1.79a7 7 0 0 0-.653-.796l.724-.69q.406.429.747.91zm.744 1.352a7 7 0 0 0-.214-.468l.893-.45a8 8 0 0 1 .45 1.088l-.95.313a7 7 0 0 0-.179-.483m.53 2.507a7 7 0 0 0-.1-1.025l.985-.17q.1.58.116 1.17zm-.131 1.538q.05-.254.081-.51l.993.123a8 8 0 0 1-.23 1.155l-.964-.267q.069-.247.12-.501m-.952 2.379q.276-.436.486-.908l.914.405q-.24.54-.555 1.038zm-.964 1.205q.183-.183.35-.378l.758.653a8 8 0 0 1-.401.432z"/><path d="M8 1a7 7 0 1 0 4.95 11.95l.707.707A8.001 8.001 0 1 1 8 0z"/><path d="M7.5 3a.5.5 0 0 1 .5.5v5.21l3.248 1.856a.5.5 0 0 1-.496.868l-3.5-2A.5.5 0 0 1 7 9V3.5a.5.5 0 0 1 .5-.5"/></symbol>
<symbol class="bi bi-hourglass-split" viewBox="0 0 16 16" id="hourglass-split"><path d="M2.5 15a.5.5 0 1 1 0-1h1v-1a4.5 4.5 0 0 1 2.557-4.06c.29-.139.443-.377.443-.59v-.7c0-.213-.154-.451-.443-.59A4.5 4.5 0 0 1 3.5 3V2h-1a.5.5 0 0 1 0-1h11a.5.5 0 0 1 0 1h-1v1a4.5 4.5 0 0 1-2.557 4.06c-.29.139-.443.377-.443.59v.7c0 .213.154.451.443.59A4.5 4.5 0 0 1 12.5 13v1h1a.5.5 0 0 1 0 1zm2-13v1c0 .537.12 1.045.337 1.5h6.326c.216-.455.337-.963.337-1.5V2zm3 6.35c0 .701-.478 1.236-1.011 1.492A3.5 3.5 0 0 0 4.5 13s.866-1.299 3-1.48zm1 0v3.17c2.134.181 3 1.48 3 1.48a3.5 3.5 0 0 0-1.989-3.158C8.978 9.586 8.5 9.052 8.5 8.351z"/></symbol>
<symbol class="bi bi-trophy-fill" viewBox="0 0 16 16" id="trophy-fill"><path d="M2.5.5A.5.5 0 0 1 3 0h10a.5.5 0 0 1 .5.5q0 .807-.034 1.536a3 3 0 1 1-1.133 5.89c-.79 1.865-1.878 2.777-2.833 3.011v2.173l1.425.356c.194.048.377.135.537.255L13.3 15.1a.5.5 0 0 1-.3.9H3a.5.5 0 0 1-.3-.9l1.838-1.379c.16-.12.343-.207.537-.255L6.5 13.11v-2.173c-.955-.234-2.043-1.146-2.833-3.012a3 3 0 1 1-1.132-5.89A33 33 0 0 1 2.5.5m.099 2.54a2 2 0 0 0 .72 3.935c-.333-1.05-.588-2.346-.72-3.935m10.083 3.935a2 2 0 0 0 .72-3.935c-.133 1.59-.388 2.885-.72 3.935"/></symbol>
<symbol class="bi bi-tag-fill" viewBox="0 0 16 16" id="tag-fill"><path d="M2 1a1 1 0 0 0-1 1v4.586a1 1 0 0 0 .293.707l7 7a1 1 0 0 0 1.414 0l4.586-4.586a1 1 0 0 0 0-1.414l-7-7A1 1 0 0 0 6.586 1zm4 3.5a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0"/></symbol>
<symbol class="bi bi-puzzle-fill" viewBox="0 0 16 16" id="puzzle-fill"><path d="M3.112 3.645A1.5 1.5 0 0 1 4.605 2H7a.5.5 0 0 1 .5.5v.382c0 .696-.497 1.182-.872 1.469a.5.5 0 0 0-.115.118l-.012.025L6.5 4.5v.003l.003.01q.005.015.036.053a.9.9 0 0 0 .27.194C7.09 4.9 7.51 5 8 5c.492 0 .912-.1 1.19-.24a.9.9 0 0 0 .271-.194.2.2 0 0 0 .036-.054l.003-.01v-.008l-.012-.025a.5.5 0 0 0-.115-.118c-.375-.287-.872-.773-.872-1.469V2.5A.5.5 0 0 1 9 2h2.395a1.5 1.5 0 0 1 1.493 1.645L12.645 6.5h.237c.195 0 .42-.147.675-.48.21-.274.528-.52.943-.52.568 0 .947.447 1.154.862C15.877 6.807 16 7.387 16 8s-.123 1.193-.346 1.638c-.207.415-.586.862-1.154.862-.415 0-.733-.246-.943-.52-.255-.333-.48-.48-.675-.48h-.237l.243 2.855A1.5 1.5 0 0 1 11.395 14H9a.5.5 0 0 1-.5-.5v-.382c0-.696.497-1.182.872-1.469a.5.5 0 0 0 .115-.118l.012-.025.001-.006v-.003l-.003-.01a.2.2 0 0 0-.036-.053.9.9 0 0 0-.27-.194C8.91 11.1 8.49 11 8 11s-.912.1-1.19.24a.9.9 0 0 0-.271.194.2.2 0 0 0-.036.054l-.003.01v.002l.001.006.012.025c.016.027.05.068.115.118.375.287.872.773.872 1.469v.382a.5.5 0 0 1-.5.5H4.605a1.5 1.5 0 0 1-1.493-1.645L3.356 9.5h-.238c-.195 0-.42.147-.675.48-.21.274-.528.52-.943.52-.568 0-.947-.447-1.154-.862C.123 9.193 0 8.613 0 8s.123-1.193.346-1.638C.553 5.947.932 5.5 1.5 5.5c.415 0 .733.246.943.52.255.333.48.48.675.48h.238z"/></symbol>
<symbol class="bi bi-ticket-perforated-fill" viewBox="0 0 16 16" id="ticket-perforated-fill"><path d="M0 4.5A1.5 1.5 0 0 1 1.5 3h13A1.5 1.5 0 0 1 16 4.5V6a.5.5 0 0 1-.5.5 1.5 1.5 0 0 0 0 3 .5.5 0 0 1 .5.5v1.5a1.5 1.5 0 0 1-1.5 1.5h-13A1.5 1.5 0 0 1 0 11.5V10a.5.5 0 0 1 .5-.5 1.5 1.5 0 1 0 0-3A.5.5 0 0 1 0 6zm4-1v1h1v-1zm1 3v-1H4v1zm7 0v-1h-1v1zm-1-2h1v-1h-1zm-6 3H4v1h1zm7 1v-1h-1v1zm-7 1H4v1h1zm7 1v-1h-1v1zm-8 1v1h1v-1zm7 1h1v-1h-1z"/></symbol>
<symbol class="bi bi-journal-text" viewBox="0 0 16 16" id="journal-text"><path d="M5 10.5a.5.5 0 0 1 .5-.5h2a.5.5 0 0 1 0 1h-2a.5.5 0 0 1-.5-.5m0-2a.5.5 0 0 1 .5-.5h5a.5.5 0 0 1 0 1h-5a.5.5 0 0 1-.5-.5m0-2a.5.5 0 0 1 .5-.5h5a.5.5 0 0 1 0 1h-5a.5.5 0 0 1-.5-.5m0-2a.5.5 0 0 1 .5-.5h5a.5.5 0 0 1 0 1h-5a.5.5 0 0 1-.5-.5"/><path d="M3 0h10a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2v-1h1v1a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V2a1 1 0 0 0-1-1H3a1 1 0 0 0-1 1v1H1V2a2 2 0 0 1 2-2"/><path d="M1 5v-.5a.5.5 0 0 1 1 0V5h.5a.5.5 0 0 1 0 1h-2a.5.5 0 0 1 0-1zm0 3v-.5a.5.5 0 0 1 1 0V8h.5a.5.5 0 0 1 0 1h-2a.5.5 0 0 1 0-1zm0 3v-.5a.5.5 0 0 1 1 0v.5h.5a.5.5 0 0 1 0 1h-2a.5.5 0 0 1 0-1z"/></symbol>
<symbol class="bi bi-inbox" viewBox="0 0 16 16" id="inbox"><path d="M4.98 4a.5.5 0 0 0-.39.188L1.54 8H6a.5.5 0 0 1 .5.5 1.5 1.5 0 1 0 3 0A.5.5 0 0 1 10 8h4.46l-3.05-3.812A.5.5 0 0 0 11.02 4zm9.954 5H10.45a2.5 2.5 0 0 1-4.9 0H1.066l.32 2.562a.5.5 0 0 0 .497.438h12.234a.5.5 0 0 0 .496-.438zM3.809 3.563A1.5 1.5 0 0 1 4.981 3h6.038a1.5 1.5 0 0 1 1.172.563l3.7 4.625a.5.5 0 0 1 .105.374l-.39 3.124A1.5 1.5 0 0 1 14.117 13H1.883a1.5 1.5 0 0 1-1.489-1.314l-.39-3.124a.5.5 0 0 1 .106-.374z"/></symbol>
<symbol class="bi bi-lightbulb-fill" viewBox="0 0 16 16" id="lightbulb-fill"><path d="M2 6a6 6 0 1 1 10.174 4.31c-.203.196-.359.4-.453.619l-.762 1.769A.5.5 0 0 1 10.5 13h-5a.5.5 0 0 1-.46-.302l-.761-1.77a2 2 0 0 0-.453-.618A5.98 5.98 0 0 1 2 6m3 8.5a.5.5 0 0 1 .5-.5h5a.5.5 0 0 1 0 1l-.224.447a1 1 0 0 1-.894.553H6.618a1 1 0 0 1-.894-.553L5.5 15a.5.5 0 0 1-.5-.5"/></symbol>
<symbol class="bi bi-star-fill" viewBox="0 0 16 16" id="star-fill"><path d="M3.612 15.443c-.386.198-.824-.149-.746-.592l.83-4.73L.173 6.765c-.329-.314-.158-.888.283-.95l4.898-.696L7.538.792c.197-.39.73-.39.927 0l2.184 4.327 4.898.696c.441.062.612.636.282.95l-3.522 3.356.83 4.73c.078.443-.36.79-.746.592L8 13.187l-4.389 2.256z"/></symbol>
<symbol class="bi bi-stars" viewBox="0 0 16 16" id="stars"><path d="M7.657 6.247c.11-.33.576-.33.686 0l.645 1.937a2.89 2.89 0 0 0 1.829 1.828l1.936.645c.33.11.33.576 0 .686l-1.937.645a2.89 2.89 0 0 0-1.828 1.829l-.645 1.936a.361.361 0 0 1-.686 0l-.645-1.937a2.89 2.89 0 0 0-1.828-1.828l-1.937-.645a.361.361 0 0 1 0-.686l1.937-.645a2.89 2.89 0 0 0 1.828-1.828zM3.794 1.148a.217.217 0 0 1 .412 0l.387 1.162c.173.518.579.924 1.097 1.097l1.162.387a.217.217 0 0 1 0 .412l-1.162.387A1.73 1.73 0 0 0 4.593 5.69l-.387 1.162a.217.217 0 0 1-.412 0L3.407 5.69A1.73 1.73 0 0 0 2.31 4.593l-1.162-.387a.217.217 0 0 1 0-.412l1.162-.387A1.73 1.73 0 0 0 3.407 2.31zM10.863.099a.145.145 0 0 1 .274 0l.258.774c.115.346.386.617.732.732l.774.258a.145.145 0 0 1 0 .274l-.774.258a1.16 1.16 0 0 0-.732.732l-.258.774a.145.145 0 0 1-.274 0l-.258-.774a1.16 1.16 0 0 0-.732-.732L9.1 2.137a.145.145 0 0 1 0-.274l.774-.258c.346-.115.617-.386.732-.732z"/></symbol>
<symbol class="bi bi-fire" viewBox="0 0 16 16" id="fire"><path d="M8 16c3.314 0 6-2 6-5.5 0-1.5-.5-4-2.5-6 .25 1.5-1.25 2-1.25 2C11 4 9 .5 6 0c.357 2 .5 4-2 6-1.25 1-2 2.729-2 4.5C2 14 4.686 16 8 16m0-1c-1.657 0-3-1-3-2.75 0-.75.25-2 1.25-3C6.125 10 7 10.5 7 10.5c-.375-1.25.5-3.25 2-3.5-.179 1-.25 2 1 3 .625.5 1 1.364 1 2.25C11 14 9.657 15 8 15"/></symbol>
<symbol class="bi bi-camera-fill" viewBox="0 0 16 16" id="camera-fill"><path d="M10.5 8.5a2.5 2.5 0 1 1-5 0 2.5 2.5 0 0 1 5 0"/><path d="M2 4a2 2 0 0 0-2 2v6a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V6a2 2 0 0 0-2-2h-1.172a2 2 0 0 1-1.414-.586l-.828-.828A2 2 0 0 0 9.172 2H6.828a2 2 0 0 0-1.414.586l-.828.828A2 2 0 0 1 3.172 4zm.5 2a.5.5 0 1 1 0-1 .5.5 0 0 1 0 1m9 2.5a3.5 3.5 0 1 1-7 0 3.5 3.5 0 0 1 7 0"/></symbol>
<symbol class="bi bi-map-fill" viewBox="0 0 16 16" id="map-fill"><path fill-rule="evenodd" d="M16 .5a.5.5 0 0 0-.598-.49L10.5.99 5.598.01a.5.5 0 0 0-.196 0l-5 1A.5.5 0 0 0 0 1.5v14a.5.5 0 0 0 .598.49l4.902-.98 4.902.98a.5.5 0 0 0 .196 0l5-1A.5.5 0 0 0 16 14.5zM5 14.09V1.11l.5-.1.5.1v12.98l-.402-.08a.5.5 0 0 0-.196 0zm5 .8V1.91l.402.08a.5.5 0 0 0 .196 0L11 1.91v12.98l-.5.1z"/></symbol>
<symbol class="bi bi-broadcast-pin" viewBox="0 0 16 16" id="broadcast-pin"><path d="M3.05 3.05a7 7 0 0 0 0 9.9.5.5 0 0 1-.707.707 8 8 0 0 1 0-11.314.5.5 0 0 1 .707.707m2.122 2.122a4 4 0 0 0 0 5.656.5.5 0 1 1-.708.708 5 5 0 0 1 0-7.072.5.5 0 0 1 .708.708m5.656-.708a.5.5 0 0 1 .708 0 5 5 0 0 1 0 7.072.5.5 0 1 1-.708-.708 4 4 0 0 0 0-5.656.5.5 0 0 1 0-.708m2.122-2.12a.5.5 0 0 1 .707 0 8 8 0 0 1 0 11.313.5.5 0 0 1-.707-.707 7 7 0 0 0 0-9.9.5.5 0 0 1 0-.707zM6 8a2 2 0 1 1 2.5 1.937V15.5a.5.5 0 0 1-1 0V9.937A2 2 0 0 1 6 8"/></symbol>
<symbol class="bi bi-moon-fill" viewBox="0 0 16 16" id="moon-fill"><path d="M6 .278a.77.77 0 0 1 .08.858 7.2 7.2 0 0 0-.878 3.46c0 4.021 3.278 7.277 7.318 7.277q.792-.001 1.533-.16a.79.79 0 0 1 .81.316.73.73 0 0 1-.031.893A8.35 8.35 0 0 1 8.344 16C3.734 16 0 12.286 0 7.71 0 4.266 2.114 1.312 5.124.06A.75.75 0 0 1 6 .278"/></symbol>
<symbol class="bi bi-arrows-fullscreen" viewBox="0 0 16 16" id="arrows-fullscreen"><path fill-rule="evenodd" d="M5.828 10.172a.5.5 0 0 0-.707 0l-4.096 4.096V11.5a.5.5 0 0 0-1 0v3.975a.5.5 0 0 0 .5.5H4.5a.5.5 0 0 0 0-1H1.732l4.096-4.096a.5.5 0 0 0 0-.707m4.344 0a.5.5 0 0 1 .707 0l4.096 4.096V11.5a.5.5 0 1 1 1 0v3.975a.5.5 0 0 1-.5.5H11.5a.5.5 0 0 1 0-1h2.768l-4.096-4.096a.5.5 0 0 1 0-.707m0-4.344a.5.5 0 0 0 .707 0l4.096-4.096V4.5a.5.5 0 1 0 1 0V.525a.5.5 0 0 0-.5-.5H11.5a.5.5 0 0 0 0 1h2.768l-4.096 4.096a.5.5 0 0 0 0 .707m-4.344 0a.5.5 0 0 1-.707 0L1.025 1.732V4.5a.5.5 0 0 1-1 0V.525a.5.5 0 0 1 .5-.5H4.5a.5.5 0 0 1 0 1H1.732l4.096 4.096a.5.5 0 0 1 0 .707"/></symbol>
<symbol class="bi bi-slash-circle" viewBox="0 0 16 16" id="slash-circle"><path d="M8 15A7 7 0 1 1 8 1a7 7 0 0 1 0 14m0 1A8 8 0 1 0 8 0a8 8 0 0 0 0 16"/><path d="M11.354 4.646a.5.5 0 0 0-.708 0l-6 6a.5.5 0 0 0 .708.708l6-6a.5.5 0 0 0 0-.708"/></symbol>
<symbol class="bi bi-circle-fill" viewBox="0 0 16 16" id="circle-fill"><circle cx="8" cy="8" r="8"/></symbol>
<symbol class="bi bi-circle" viewBox="0 0 16 16" id="circle"><path d="M8 15A7 7 0 1 1 8 1a7 7 0 0 1 0 14m0 1A8 8 0 1 0 8 0a8 8 0 0 0 0 16"/></symbol>
<symbol class="bi bi-check-circle-fill" viewBox="0 0 16 16" id="check-circle-fill"><path d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0m-3.97-3.03a.75.75 0 0 0-1.08.022L7.477 9.417 5.384 7.323a.75.75 0 0 0-1.06 1.06L6.97 11.03a.75.75 0 0 0 1.079-.02l3.992-4.99a.75.75 0 0 0-.01-1.05z"/></symbol>
<symbol class="bi bi-exclamation-triangle-fill" viewBox="0 0 16 16" id="exclamation-triangle-fill"><path d="M8.982 1.566a1.13 1.13 0 0 0-1.96 0L.165 13.233c-.457.778.091 1.767.98 1.767h13.713c.889 0 1.438-.99.98-1.767zM8 5c.535 0 .954.462.9.995l-.35 3.507a.552.552 0 0 1-1.1 0L7.1 5.995A.905.905 0 0 1 8 5m.002 6a1 1 0 1 1 0 2 1 1 0 0 1 0-2"/></symbol>
<symbol class="bi bi-x-circle-fill" viewBox="0 0 16 16" id="x-circle-fill"><path d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0M5.354 4.646a.5.5 0 1 0-.708.708L7.293 8l-2.647 2.646a.5.5 0 0 0 .708.708L8 8.707l2.646 2.647a.5.5 0 0 0 .708-.708L8.707 8l2.647-2.646a.5.5 0 0 0-.708-.708L8 7.293z"/></symbol>
<symbol class="bi bi-question-circle" viewBox="0 0 16 16" id="question-circle"><path d="M8 15A7 7 0 1 1 8 1a7 7 0 0 1 0 14m0 1A8 8 0 1 0 8 0a8 8 0 0 0 0 16"/><path d="M5.255 5.786a.237.237 0 0 0 .241.247h.825c.138 0 .248-.113.266-.25.09-.656.54-1.134 1.342-1.134.686 0 1.314.343 1.314 1.168 0 .635-.374.927-.965 1.371-.673.489-1.206 1.06-1.168 1.987l.003.217a.25.25 0 0 0 .25.246h.811a.25.25 0 0 0 .25-.25v-.105c0-.718.273-.927 1.01-1.486.609-.463 1.244-.977 1.244-2.056 0-1.511-1.276-2.241-2.673-2.241-1.267 0-2.655.59-2.75 2.286m1.557 5.763c0 .533.425.927 1.01.927.609 0 1.028-.394 1.028-.927 0-.552-.42-.94-1.029-.94-.584 0-1.009.388-1.009.94"/></symbol>
<symbol class="bi bi-download" viewBox="0 0 16 16" id="download"><path d="M.5 9.9a.5.5 0 0 1 .5.5v2.5a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-2.5a.5.5 0 0 1 1 0v2.5a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2v-2.5a.5.5 0 0 1 .5-.5"/><path d="M7.646 11.854a.5.5 0 0 0 .708 0l3-3a.5.5 0 0 0-.708-.708L8.5 10.293V1.5a.5.5 0 0 0-1 0v8.793L5.354 8.146a.5.5 0 1 0-.708.708z"/></symbol>
<symbol class="bi bi-arrow-left" viewBox="0 0 16 16" id="arrow-left"><path fill-rule="evenodd" d="M15 8a.5.5 0 0 0-.5-.5H2.707l3.147-3.146a.5.5 0 1 0-.708-.708l-4 4a.5.5 0 0 0 0 .708l4 4a.5.5 0 0 0 .708-.708L2.707 8.5H14.5A.5.5 0 0 0 15 8"/></symbol>
<symbol class="bi bi-arrow-right" viewBox="0 0 16 16" id="arrow-right"><path fill-rule="evenodd" d="M1 8a.5.5 0 0 1 .5-.5h11.793l-3.147-3.146a.5.5 0 0 1 .708-.708l4 4a.5.5 0 0 1 0 .708l-4 4a.5.5 0 0 1-.708-.708L13.293 8.5H1.5A.5.5 0 0 1 1 8"/></symbol>
<symbol class="bi bi-arrow-counterclockwise" viewBox="0 0 16 16" id="arrow-counterclockwise"><path fill-rule="evenodd" d="M8 3a5 5 0 1 1-4.546 2.914.5.5 0 0 0-.908-.417A6 6 0 1 0 8 2z"/><path d="M8 4.466V.534a.25.25 0 0 0-.41-.192L5.23 2.308a.25.25 0 0 0 0 .384l2.36 1.966A.25.25 0 0 0 8 4.466"/></symbol>
<symbol class="bi bi-check2" viewBox="0 0 16 16" id="check2"><path d="M13.854 3.646a.5.5 0 0 1 0 .708l-7 7a.5.5 0 0 1-.708 0l-3.5-3.5a.5.5 0 1 1 .708-.708L6.5 10.293l6.646-6.647a.5.5 0 0 1 .708 0"/></symbol>
</svg>

After

Width:  |  Height:  |  Size: 33 KiB