Correção Dash Pendentes e falhas

This commit is contained in:
2026-06-22 17:47:05 -03:00
parent b742b2c2e3
commit 56b3e146bf
3 changed files with 96 additions and 29 deletions

View File

@@ -80,14 +80,19 @@ class DashboardController < ApplicationController
pagas = Entrega.da_conta_gade.pagas.no_periodo_checkout(@periodo_inicio, @periodo_fim) pagas = Entrega.da_conta_gade.pagas.no_periodo_checkout(@periodo_inicio, @periodo_fim)
pagas = pagas.da_operacoes(@operacao_filtro) if @operacao_filtro.present? pagas = pagas.da_operacoes(@operacao_filtro) if @operacao_filtro.present?
# PENDENTES (não concluídas) pela data planejada — não têm checkout. # PENDENTES (em aberto, nem concluídas nem falhadas) pela data planejada.
pendentes = Entrega.da_conta_gade.pendentes.no_periodo(@periodo_inicio, @periodo_fim) pendentes = Entrega.da_conta_gade.pendentes.no_periodo(@periodo_inicio, @periodo_fim)
pendentes = pendentes.da_operacoes(@operacao_filtro) if @operacao_filtro.present? pendentes = pendentes.da_operacoes(@operacao_filtro) if @operacao_filtro.present?
# FALHADAS (insucesso) pela data planejada — também não têm checkout.
falhadas = Entrega.da_conta_gade.falhadas.no_periodo(@periodo_inicio, @periodo_fim)
falhadas = falhadas.da_operacoes(@operacao_filtro) if @operacao_filtro.present?
# Totais gerais # Totais gerais
@entregas_pagas = pagas.count @entregas_pagas = pagas.count
@entregas_pendentes = pendentes.count @entregas_pendentes = pendentes.count
@total_entregas = @entregas_pagas + @entregas_pendentes @entregas_falhadas = falhadas.count
@total_entregas = @entregas_pagas + @entregas_pendentes + @entregas_falhadas
# Configurações de preço # Configurações de preço
config = Configuracao.mapa_de_precos config = Configuracao.mapa_de_precos

View File

@@ -13,12 +13,20 @@ class Entrega < ApplicationRecord
true true
end end
# Status que o SimpleRoute grava para entregas SEM SUCESSO (falha/insucesso).
# AJUSTE aqui se o valor real for diferente (ex.: 'cancelled', 'not_delivered').
STATUS_FALHA = %w[failed].freeze
# ── Scopes ────────────────────────────────────────────────── # ── Scopes ──────────────────────────────────────────────────
scope :concluidas, -> { where(status: 'completed') } scope :concluidas, -> { where(status: 'completed') }
scope :com_checkout, -> { where.not(checkout: nil) } scope :com_checkout, -> { where.not(checkout: nil) }
# Pagamento = entrega concluída com checkout registrado # Pagamento = entrega concluída com checkout registrado
scope :pagas, -> { concluidas.com_checkout } scope :pagas, -> { concluidas.com_checkout }
scope :pendentes, -> { where.not(status: 'completed') } # Falhadas = entregas que não tiveram sucesso (status de falha)
scope :falhadas, -> { where(status: STATUS_FALHA) }
# Pendentes = nem concluídas nem falhadas (ainda em aberto). Exclui as falhadas
# para que entregues/pendentes/falhadas não se sobreponham no dashboard.
scope :pendentes, -> { where.not(status: ['completed'] + STATUS_FALHA) }
# planned_date é timestamp: usamos o dia inteiro do limite superior para não # planned_date é timestamp: usamos o dia inteiro do limite superior para não
# descartar as entregas do último dia do período (causava dashboard zerado e # descartar as entregas do último dia do período (causava dashboard zerado e

View File

@@ -5,8 +5,41 @@
<%# alterado pelo JS, sem reexecutar scripts) e só voltava com F5. %> <%# alterado pelo JS, sem reexecutar scripts) e só voltava com F5. %>
<% content_for :head do %> <% content_for :head do %>
<meta name="turbo-cache-control" content="no-cache"> <meta name="turbo-cache-control" content="no-cache">
<%# Flatpickr — CSS no <head> (evita flash sem estilo) + tema escuro oficial %>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/flatpickr@4.6.13/dist/flatpickr.min.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/flatpickr@4.6.13/dist/themes/dark.css">
<style>
/* Ajustes do calendário ao tema Reem (preto/laranja) */
.flatpickr-calendar { background: #1a1a1a; border: 1px solid rgba(255,255,255,.1); box-shadow: 0 10px 30px rgba(0,0,0,.5); }
.flatpickr-months, .flatpickr-weekdays, .flatpickr-weekday { background: #1a1a1a; color: #9ca3af; }
.flatpickr-current-month, .flatpickr-monthDropdown-months, .numInput { color: #fff; }
.flatpickr-day { color: #e5e7eb; }
.flatpickr-day:hover { background: #2a2a2a; border-color: #2a2a2a; }
.flatpickr-day.today { border-color: #f97316; }
.flatpickr-day.selected,
.flatpickr-day.startRange,
.flatpickr-day.endRange,
.flatpickr-day.selected:hover,
.flatpickr-day.startRange:hover,
.flatpickr-day.endRange:hover {
background: #f97316; border-color: #f97316; color: #0a0a0a;
}
.flatpickr-day.inRange { background: rgba(249,115,22,.18); border-color: rgba(249,115,22,.18); box-shadow: -5px 0 0 rgba(249,115,22,.18), 5px 0 0 rgba(249,115,22,.18); }
.flatpickr-day.flatpickr-disabled, .flatpickr-day.prevMonthDay, .flatpickr-day.nextMonthDay { color: #4b5563; }
.flatpickr-months .flatpickr-prev-month svg, .flatpickr-months .flatpickr-next-month svg { fill: #f97316; }
</style>
<% end %> <% end %>
<%# Quick-ranges no formato YYYY-MM-DD para os atalhos do filtro %>
<% hoje = Date.current %>
<% atalhos = {
'Hoje' => [hoje, hoje],
'7 dias' => [hoje - 6, hoje],
'Este mês' => [hoje.beginning_of_month, hoje],
'30 dias' => [hoje - 29, hoje]
} %>
<div class="space-y-6"> <div class="space-y-6">
<%# Header + filtro por faixa de datas %> <%# Header + filtro por faixa de datas %>
@@ -20,17 +53,28 @@
</div> </div>
<%# 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 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 text-orange-500">📅</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
text-white text-sm w-[240px] focus:outline-none focus:border-orange-500 placeholder-gray-500"> text-white text-sm w-full sm:w-[240px] focus:outline-none focus:border-orange-500 placeholder-gray-500">
</div> </div>
<input type="hidden" name="inicio" id="inicio-hidden" value="<%= @periodo_inicio.strftime('%Y-%m-%d') %>"> <input type="hidden" name="inicio" id="inicio-hidden" value="<%= @periodo_inicio.strftime('%Y-%m-%d') %>">
<input type="hidden" name="fim" id="fim-hidden" value="<%= @periodo_fim.strftime('%Y-%m-%d') %>"> <input type="hidden" name="fim" id="fim-hidden" value="<%= @periodo_fim.strftime('%Y-%m-%d') %>">
<%# Atalhos rápidos de período %>
<div class="flex flex-wrap items-center gap-1">
<% atalhos.each do |label, (ini, fim)| %>
<% ativo = @periodo_inicio == ini && @periodo_fim == fim %>
<%= link_to label,
dashboard_path(inicio: ini.strftime('%Y-%m-%d'), fim: fim.strftime('%Y-%m-%d'), operacoes: @operacao_filtro),
data: { turbo: false },
class: "px-3 py-2.5 rounded-xl text-sm whitespace-nowrap border #{ativo ? 'bg-orange-500 text-black border-orange-500 font-bold' : 'bg-[#1a1a1a] text-gray-300 border-white/10 hover:border-orange-500'}" %>
<% end %>
</div>
<%# Filtro de operação — multi-seleção; submete o form ao mudar %> <%# Filtro de operação — multi-seleção; submete o form ao mudar %>
<% if @operacoes.any? %> <% if @operacoes.any? %>
<details class="relative" id="operacao-dropdown"> <details class="relative" id="operacao-dropdown">
@@ -105,10 +149,12 @@
<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">📦 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 items-center gap-2 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>
<span class="text-gray-400">·</span> <span class="text-gray-400">·</span>
<span class="text-yellow-500"><%= @entregas_pendentes %> pendentes</span> <span class="text-yellow-500"><%= @entregas_pendentes %> pendentes</span>
<span class="text-gray-400">·</span>
<span class="text-red-400"><%= @entregas_falhadas %> falhadas</span>
</div> </div>
</div> </div>
@@ -376,14 +422,15 @@
</div> </div>
<%# Flatpickr — date range picker (CDN, mesmo padrão do Chart.js) %> <%# Flatpickr — date range picker (JS via CDN; CSS já carregado no <head>) %>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/flatpickr@4.6.13/dist/flatpickr.min.css">
<script src="https://cdn.jsdelivr.net/npm/flatpickr@4.6.13/dist/flatpickr.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/flatpickr@4.6.13/dist/flatpickr.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/flatpickr@4.6.13/dist/l10n/pt.js"></script> <script src="https://cdn.jsdelivr.net/npm/flatpickr@4.6.13/dist/l10n/pt.js"></script>
<script> <script>
(function () { (function () {
function initPeriodo() {
const input = document.getElementById('periodo-range'); const input = document.getElementById('periodo-range');
if (!input || typeof flatpickr === 'undefined') return; if (!input || typeof flatpickr === 'undefined') return;
if (input._flatpickr) return; // já inicializado nesta página
const inicio = document.getElementById('inicio-hidden').value; const inicio = document.getElementById('inicio-hidden').value;
const fim = document.getElementById('fim-hidden').value; const fim = document.getElementById('fim-hidden').value;
@@ -401,13 +448,20 @@
defaultDate: [inicio, fim], defaultDate: [inicio, fim],
maxDate: 'today', maxDate: 'today',
onClose: function (selectedDates) { onClose: function (selectedDates) {
if (selectedDates.length === 2) { if (selectedDates.length === 0) return;
document.getElementById('inicio-hidden').value = toISO(selectedDates[0]); // 1 dia selecionado → início e fim iguais; 2 dias → faixa completa.
document.getElementById('fim-hidden').value = toISO(selectedDates[1]); const ini = selectedDates[0];
const f = selectedDates[selectedDates.length - 1];
document.getElementById('inicio-hidden').value = toISO(ini);
document.getElementById('fim-hidden').value = toISO(f);
document.getElementById('dashboard-filtro-form').requestSubmit(); document.getElementById('dashboard-filtro-form').requestSubmit();
} }
}
}); });
}
// Executa no carregamento inicial e também em navegação Turbo (sem F5).
initPeriodo();
document.addEventListener('turbo:load', initPeriodo);
})(); })();
</script> </script>