Correção Dash Pendentes e falhas
This commit is contained in:
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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,38 +422,46 @@
|
|||||||
|
|
||||||
</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 () {
|
||||||
const input = document.getElementById('periodo-range');
|
function initPeriodo() {
|
||||||
if (!input || typeof flatpickr === 'undefined') return;
|
const input = document.getElementById('periodo-range');
|
||||||
|
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;
|
||||||
|
|
||||||
// formata Date local → YYYY-MM-DD (evita deslocamento de fuso do toISOString)
|
// formata Date local → YYYY-MM-DD (evita deslocamento de fuso do toISOString)
|
||||||
const toISO = (d) =>
|
const toISO = (d) =>
|
||||||
d.getFullYear() + '-' +
|
d.getFullYear() + '-' +
|
||||||
String(d.getMonth() + 1).padStart(2, '0') + '-' +
|
String(d.getMonth() + 1).padStart(2, '0') + '-' +
|
||||||
String(d.getDate()).padStart(2, '0');
|
String(d.getDate()).padStart(2, '0');
|
||||||
|
|
||||||
flatpickr(input, {
|
flatpickr(input, {
|
||||||
mode: 'range',
|
mode: 'range',
|
||||||
locale: 'pt',
|
locale: 'pt',
|
||||||
dateFormat: 'd/m/Y',
|
dateFormat: 'd/m/Y',
|
||||||
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>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user