From 8689b7ec0d4bc3c3467049174160249af54dd923 Mon Sep 17 00:00:00 2001 From: root Date: Tue, 17 Feb 2026 10:02:37 -0500 Subject: [PATCH] feat: skeleton shimmer for Client 360 + fix Chart.js event error on BI - Add skeleton loading to admin-cliente.js: profile card, 12 hero KPIs, health/risk/netting values, 9 charts, transaction table - Fix BI Chart.js handleEvent crash: use absolute-positioned skeleton overlays instead of display:none on canvases - Fix setPreset null pointer on preset button querySelector Co-Authored-By: Claude Opus 4.6 --- src/admin-bi.js | 35 +++++++------ src/admin-cliente.js | 117 +++++++++++++++++++++++++++---------------- 2 files changed, 92 insertions(+), 60 deletions(-) diff --git a/src/admin-bi.js b/src/admin-bi.js index 0a203c2..1e36dc5 100644 --- a/src/admin-bi.js +++ b/src/admin-bi.js @@ -812,7 +812,7 @@ function buildAdminBIHTML(user) { .skel-value { height: 32px; width: 55%; } .skel-badge { height: 18px; width: 70px; } .skel-text { height: 14px; width: 80%; margin: 4px 0; } - .skel-chart { height: 100%; width: 100%; min-height: 180px; border-radius: 12px; } + .skel-chart { height: 100%; width: 100%; min-height: 180px; border-radius: 12px; position: absolute; top: 0; left: 0; z-index: 2; } .skel-row { height: 32px; margin: 6px 0; width: 100%; } .skel-gauge { height: 40px; width: 90px; } `; @@ -909,11 +909,11 @@ ${buildHeader({ role: role, userName: user.nome, activePage: 'bi' })}

Revenue por Corredor

-
+

Spread Medio + Volume Diario --

-
+
@@ -928,7 +928,7 @@ ${buildHeader({ role: role, userName: user.nome, activePage: 'bi' })} -
+
@@ -968,7 +968,7 @@ ${buildHeader({ role: role, userName: user.nome, activePage: 'bi' })}

Volume por Corredor

-
+

Ranking Agentes

@@ -991,7 +991,7 @@ ${buildHeader({ role: role, userName: user.nome, activePage: 'bi' })}

Forecast: Historical + Predicted Volume

Loading forecast...
-
+
@@ -1009,7 +1009,7 @@ ${buildHeader({ role: role, userName: user.nome, activePage: 'bi' })}
-
+

Resumo Netting

@@ -1077,11 +1077,11 @@ ${buildHeader({ role: role, userName: user.nome, activePage: 'bi' })}

Receita por Produto ao Longo do Tempo

-
+

Composicao de Receita por Produto

-
+
@@ -1131,11 +1131,11 @@ ${buildHeader({ role: role, userName: user.nome, activePage: 'bi' })}

Waterfall de Receita

-
+

Distribuicao por Segmento

-
+
@@ -1147,12 +1147,12 @@ ${buildHeader({ role: role, userName: user.nome, activePage: 'bi' })}

Cross-sell CambioPay vs Checkout

-
+

Maturidade de Clientes

-
+
@@ -1231,11 +1231,9 @@ const fmtPct = (curr, prev) => { }; // === Skeleton helpers === -function showChart(skelId, canvasId) { +function showChart(skelId) { var s = document.getElementById(skelId); if (s) s.remove(); - var c = document.getElementById(canvasId); - if (c) c.style.display = ''; } // === Date & Filter Logic === @@ -1260,8 +1258,9 @@ function setPreset(preset) { } document.getElementById('dateStart').value = start; document.getElementById('dateEnd').value = today; - document.querySelectorAll('.preset-btn').forEach(b => b.classList.remove('active')); - document.querySelector('[data-preset="'+preset+'"]').classList.add('active'); + document.querySelectorAll('.preset-btn[data-preset]').forEach(b => b.classList.remove('active')); + var _pb = document.querySelector('[data-preset="'+preset+'"]'); + if (_pb) _pb.classList.add('active'); currentStart = start; currentEnd = today; loadAll(); } diff --git a/src/admin-cliente.js b/src/admin-cliente.js index 610f692..3db9664 100644 --- a/src/admin-cliente.js +++ b/src/admin-cliente.js @@ -330,6 +330,28 @@ function buildAdminClienteHTML(user) { [data-theme="dark"] .date-inputs input[type="date"] { background: var(--card); color: var(--text); border-color: var(--border); } [data-theme="dark"] .data-table tr:hover td { background: rgba(255,255,255,0.03); } + /* === Skeleton Loading Shimmer === */ + @keyframes shimmer { + 0% { background-position: -400px 0; } + 100% { background-position: 400px 0; } + } + .skel { + background: linear-gradient(90deg, var(--card-bg,var(--card)) 25%, var(--border) 50%, var(--card-bg,var(--card)) 75%); + background-size: 800px 100%; + animation: shimmer 1.5s infinite ease-in-out; + border-radius: 6px; + display: inline-block; + } + .skel-value { height: 32px; width: 55%; } + .skel-badge { height: 18px; width: 70px; } + .skel-text { height: 14px; width: 80%; margin: 4px 0; } + .skel-chart { height: 100%; width: 100%; min-height: 180px; border-radius: 12px; position: absolute; top: 0; left: 0; z-index: 2; } + .skel-row { height: 32px; margin: 6px 0; width: 100%; } + .skel-gauge { height: 40px; width: 90px; } + .skel-avatar { width: 56px; height: 56px; border-radius: 50%; } + .skel-name { height: 22px; width: 160px; } + .skel-stat { height: 18px; width: 70px; } + /* === Merchant / Checkout === */ .merchant-badge { display: none; padding: 4px 12px; border-radius: 8px; font-size: 11px; font-weight: 800; @@ -397,23 +419,23 @@ ${buildHeader({ role: role, userName: user.nome, activePage: 'cliente' })}
-
--
+
-
--
-
--
+
+
MERCHANT
-
Primeira Op
--
-
Ultima Op
--
-
Meses Ativo
--
-
LTV (Receita)
--
-
Vol. Lifetime
--
-
Ops Lifetime
--
+
Primeira Op
+
Ultima Op
+
Meses Ativo
+
LTV (Receita)
+
Vol. Lifetime
+
Ops Lifetime
-
--
+
Health
@@ -440,12 +462,12 @@ ${buildHeader({ role: role, userName: user.nome, activePage: 'cliente' })}
-
Volume USD
--
--
-
Transacoes
--
--
-
Receita USD
--
--
-
Ticket Medio
--
USD / operacao
-
ARPA
--
receita / mes
-
Spread Medio
--
% ponderado
+
Volume USD
+
Transacoes
+
Receita USD
+
Ticket Medio
USD / operacao
+
ARPA
receita / mes
+
Spread Medio
% ponderado
@@ -455,12 +477,12 @@ ${buildHeader({ role: role, userName: user.nome, activePage: 'cliente' })} CambioCheckout (Merchant)
-
Checkout Volume
--
--
-
Payers Unicos
--
pagadores distintos
-
Receita Checkout
--
--
-
Checkout Ops
--
--
-
Ticket Checkout
--
USD / operacao
-
Spread Checkout
--
% medio
+
Checkout Volume
+
Payers Unicos
pagadores distintos
+
Receita Checkout
+
Checkout Ops
+
Ticket Checkout
USD / operacao
+
Spread Checkout
% medio
@@ -469,7 +491,7 @@ ${buildHeader({ role: role, userName: user.nome, activePage: 'cliente' })}

Checkout Mensal: Volume + Payers

-
+

Top 10 Pagadores

@@ -489,7 +511,7 @@ ${buildHeader({ role: role, userName: user.nome, activePage: 'cliente' })}
-
--
--
+
Volume
0
@@ -502,22 +524,22 @@ ${buildHeader({ role: role, userName: user.nome, activePage: 'cliente' })}

Indicadores de Churn

-
--
+
-
Variacao Volume--
-
Variacao Transacoes--
-
Dias Inativo--
-
Intervalo Medio--
+
Variacao Volume
+
Variacao Transacoes
+
Dias Inativo
+
Intervalo Medio

Netting & Posicao

-
Saida (BRL→USD)--
-
Entrada (USD→BRL)--
-
Posicao Liquida--
-
Eficiencia Netting
--%
+
Saida (BRL→USD)
+
Entrada (USD→BRL)
+
Posicao Liquida
+
Eficiencia Netting
@@ -530,11 +552,11 @@ ${buildHeader({ role: role, userName: user.nome, activePage: 'cliente' })}

Receita USD Mensal + Volume

-
+

Crescimento MoM %

-
+
@@ -552,7 +574,7 @@ ${buildHeader({ role: role, userName: user.nome, activePage: 'cliente' })}
-
+
@@ -563,11 +585,11 @@ ${buildHeader({ role: role, userName: user.nome, activePage: 'cliente' })}

BRL→USD vs USD→BRL

-
+

Tendencia Spread % por Fluxo

-
+
@@ -601,7 +623,7 @@ ${buildHeader({ role: role, userName: user.nome, activePage: 'cliente' })} Status Provider - -- +
@@ -613,9 +635,9 @@ ${buildHeader({ role: role, userName: user.nome, activePage: 'cliente' })} Insights Comportamentais
-

Atividade por Dia da Semana

-

Ticket Medio Mensal

-

Providers / Metodos

+

Atividade por Dia da Semana

+

Ticket Medio Mensal

+

Providers / Metodos

@@ -686,6 +708,12 @@ function applyChartDefaults(t) { Chart.defaults.plugins.tooltip.borderWidth = 1; } +// === Skeleton helpers === +function showChart(skelId) { + var s = document.getElementById(skelId); + if (s) s.remove(); +} + // === Charts === var chartTimeline, chartFlowDonut, chartSpreadTrend, chartDow, chartAvgSize, chartProvider, chartMonthlyRev, chartMoM, chartCheckoutMonthly; function destroyAllCharts() { @@ -1015,6 +1043,10 @@ function renderIntelligence(d, t) { function renderCharts(d, t) { if (typeof Chart === 'undefined') return; destroyAllCharts(); + showChart('skelMonthlyRev'); showChart('skelMoM'); + showChart('skelTimeline'); showChart('skelFlowDonut'); + showChart('skelSpreadTrend'); showChart('skelDow'); + showChart('skelAvgSize'); showChart('skelProvider'); renderMonthlyRevenue(d, t); renderMoMChart(d, t); renderTimeline(d, t); @@ -1196,6 +1228,7 @@ function renderCheckoutKPIs(d) { document.getElementById('ckOpsBadge').className = 'hero-badge ' + bo.cls; document.getElementById('ckOpsBadge').textContent = bo.text; } function renderCheckoutMonthly(d, t) { + showChart('skelCheckoutMonthly'); if (!d.merchant || !d.merchant.monthly || !d.merchant.monthly.length) return; var m = d.merchant.monthly; if (chartCheckoutMonthly) chartCheckoutMonthly.destroy();