-
❌ Total de Recusas
-
<%= metricas.recusas %>
-
- <%= pct_total.(metricas.recusas) %>% do total
-
+
+
Total de Recusas
+
<%= metricas.recusas %>
+
<%= pct_total.(metricas.recusas) %>% do total
-
-
⏳ Entregas Pendentes
-
<%= metricas.pendentes %>
-
- <%= pct_total.(metricas.pendentes) %>% do total
-
+
+
Entregas Pendentes
+
<%= metricas.pendentes %>
+
<%= pct_total.(metricas.pendentes) %>% do total
diff --git a/app/views/operacoes_dashboard/index.html.erb b/app/views/operacoes_dashboard/index.html.erb
index 0b5da1f..6391d22 100644
--- a/app/views/operacoes_dashboard/index.html.erb
+++ b/app/views/operacoes_dashboard/index.html.erb
@@ -310,6 +310,37 @@
});
});
+ // Plugin: desenha o valor centralizado DENTRO de cada segmento da barra
+ // (pula segmentos baixos demais e valores zero).
+ const barValueLabels = {
+ id: 'barValueLabels',
+ afterDatasetsDraw: function (chart) {
+ const g = chart.ctx;
+ g.save();
+ g.font = '600 11px system-ui, -apple-system, sans-serif';
+ g.fillStyle = '#ffffff';
+ g.textAlign = 'center';
+ g.textBaseline = 'middle';
+ chart.data.datasets.forEach(function (ds, i) {
+ const meta = chart.getDatasetMeta(i);
+ if (meta.hidden) return;
+ meta.data.forEach(function (bar, idx) {
+ const v = Number(ds.data[idx]) || 0;
+ if (!v) return;
+ const p = bar.getProps(['x', 'y', 'base'], true);
+ if (Math.abs(p.base - p.y) < 16) return; // segmento pequeno demais
+ g.fillText(String(v), p.x, (p.y + p.base) / 2);
+ });
+ });
+ g.restore();
+ }
+ };
+
+ // Total do dia (soma das séries) para o % no tooltip.
+ const totalDoDia = function (datasets, idx) {
+ return datasets.reduce(function (s, d) { return s + (Number(d.data[idx]) || 0); }, 0);
+ };
+
document.querySelectorAll('canvas.dash-bars').forEach(function (ctx) {
Chart.getChart(ctx)?.destroy();
new Chart(ctx, {
@@ -321,8 +352,10 @@
{ label: 'Falhas', data: JSON.parse(ctx.dataset.failed), backgroundColor: COR_FAIL }
]
},
+ plugins: [barValueLabels],
options: {
responsive: true, maintainAspectRatio: false,
+ interaction: { mode: 'index', intersect: false },
onHover: cursorPonteiro,
onClick: function (e, els) {
if (!els.length) return;
@@ -334,7 +367,32 @@
x: { stacked: true, ticks: { color: '#6b7280', maxTicksLimit: 12 }, grid: { color: 'rgba(255,255,255,0.04)' } },
y: { stacked: true, beginAtZero: true, ticks: { color: '#6b7280' }, grid: { color: 'rgba(255,255,255,0.04)' } }
},
- plugins: { legend: { labels: { color: '#9ca3af' } } }
+ plugins: {
+ legend: { labels: { color: '#9ca3af' } },
+ // Card de hover no tema escuro do app: data + Completas/Falhas (n e %) + Total.
+ tooltip: {
+ mode: 'index', intersect: false,
+ backgroundColor: '#1f1f1f',
+ borderColor: 'rgba(255,255,255,0.1)', borderWidth: 1,
+ titleColor: '#ffffff', bodyColor: '#d1d5db', footerColor: '#9ca3af',
+ padding: 12, cornerRadius: 12, boxPadding: 4, usePointStyle: true,
+ titleFont: { weight: '700' }, footerFont: { weight: '600' },
+ callbacks: {
+ title: function (items) { return items.length ? items[0].label : ''; },
+ label: function (item) {
+ const total = totalDoDia(item.chart.data.datasets, item.dataIndex);
+ const v = Number(item.raw) || 0;
+ const pct = total ? (v * 100 / total).toFixed(2) : '0.00';
+ return ' ' + item.dataset.label + ': ' + v + ' (' + pct + '%)';
+ },
+ footer: function (items) {
+ if (!items.length) return '';
+ const total = totalDoDia(items[0].chart.data.datasets, items[0].dataIndex);
+ return 'Total: ' + total + ' (100%)';
+ }
+ }
+ }
+ }
}
});
});