diff --git a/server.js b/server.js index 47f7130..082e2e9 100644 --- a/server.js +++ b/server.js @@ -11,7 +11,7 @@ const express = require('express'); const session = require('express-session'); const path = require('path'); const { authenticate, requireAuth, requireRole, createAgente, createUser } = require('./src/auth'); -const { fetchTransacoes, fetchAllTransacoes, serialize, fetchDailyStats, fetchKPIs, fetchTrend30Days, fetchTopAgentes, fetchTrendByPeriod, fetchKPIsByPeriod, fetchBIData, fetchRevenueAnalytics, fetchClientList, fetchClientProfile, fetchClientData } = require('./src/queries'); +const { fetchTransacoes, fetchAllTransacoes, serialize, fetchDailyStats, fetchKPIs, fetchTrend30Days, fetchTopAgentes, fetchTrendByPeriod, fetchKPIsByPeriod, fetchBIData, fetchRevenueAnalytics, fetchBIStrategic, fetchClientList, fetchClientProfile, fetchClientData } = require('./src/queries'); const { buildHTML } = require('./src/dashboard'); const { buildAdminHTML } = require('./src/admin-panel'); const { buildAdminHomeHTML } = require('./src/admin-home'); @@ -370,6 +370,18 @@ app.get('/admin/api/bi/revenue', requireRole('admin'), async (req, res) => { } }); +app.get('/admin/api/bi/strategic', requireRole('admin'), async (req, res) => { + try { + const { start, end } = req.query; + if (!start || !end) return res.status(400).json({ error: 'start and end required' }); + const data = await fetchBIStrategic(start, end); + res.json(data); + } catch (err) { + console.error('Strategic BI API error:', err); + res.status(500).json({ error: err.message }); + } +}); + // --- Admin Cliente Dashboard (admin only) --- app.get('/admin/cliente', requireRole('admin'), (req, res) => { try { diff --git a/src/admin-bi.js b/src/admin-bi.js index 71ff7e6..3d02d89 100644 --- a/src/admin-bi.js +++ b/src/admin-bi.js @@ -17,8 +17,19 @@ function buildAdminBIHTML(user) { /* Smooth scroll for anchor navigation */ html { scroll-behavior: smooth; scroll-padding-top: 20px; } - /* === TRADING CONSOLE: Permanent dark terminal theme === */ + /* === TRADING CONSOLE: Light Mode (professional clean) === */ body.trading-console { + --tc-accent: #1E8E3E; + --tc-accent-bg: rgba(30,142,62,0.08); + --tc-accent-border: rgba(30,142,62,0.15); + --tc-glass: rgba(255,255,255,0.85); + --tc-grid: rgba(0,0,0,0.06); + background: var(--bg); + color: var(--text); + } + + /* === TRADING CONSOLE: Dark Mode (Bloomberg terminal) === */ + [data-theme="dark"] body.trading-console { --bg: #0D1117; --card: #131A24; --text: #E2E8F0; @@ -37,175 +48,257 @@ function buildAdminBIHTML(user) { --purple-bg: rgba(188,140,255,0.08); --admin-accent: #00FF88; --admin-bg: rgba(0,255,136,0.05); + --tc-accent: #00FF88; + --tc-accent-bg: rgba(0,255,136,0.08); + --tc-accent-border: rgba(0,255,136,0.15); + --tc-glass: rgba(15,25,35,0.92); + --tc-grid: rgba(0,255,136,0.06); background: #0A0F18 !important; color: var(--text); color-scheme: dark; } - /* Console Cards */ + /* Console Cards - Light */ body.trading-console .hero-card, body.trading-console .chart-card, body.trading-console .metric-card, body.trading-console .filter-bar { + background: var(--card); + border: 1px solid var(--border); + box-shadow: 0 2px 8px rgba(0,0,0,0.06); + } + /* Console Cards - Dark */ + [data-theme="dark"] body.trading-console .hero-card, + [data-theme="dark"] body.trading-console .chart-card, + [data-theme="dark"] body.trading-console .metric-card, + [data-theme="dark"] body.trading-console .filter-bar { background: rgba(255,255,255,0.03); border: 1px solid rgba(0,255,136,0.1); box-shadow: 0 2px 12px rgba(0,0,0,0.3), inset 0 1px 0 rgba(0,255,136,0.05); } - /* Console Values - monospace terminal font */ + /* Console Values - Light: clean professional */ body.trading-console .hero-value { + font-variant-numeric: tabular-nums; + } + /* Console Values - Dark: monospace terminal */ + [data-theme="dark"] body.trading-console .hero-value { font-family: 'SF Mono', 'Fira Code', 'Consolas', monospace; text-shadow: 0 0 8px rgba(226,232,240,0.15); } - /* Console Section Titles */ + /* Console Section Titles - Light */ body.trading-console .section-title { + color: var(--text-secondary); + letter-spacing: 1px; + } + body.trading-console .section-title .icon { + background: var(--tc-accent-bg) !important; + color: var(--tc-accent) !important; + } + /* Console Section Titles - Dark */ + [data-theme="dark"] body.trading-console .section-title { color: rgba(0,255,136,0.7); font-family: 'SF Mono', 'Fira Code', 'Consolas', monospace; letter-spacing: 2px; } - body.trading-console .section-title .icon { + [data-theme="dark"] body.trading-console .section-title .icon { background: rgba(0,255,136,0.08) !important; color: #00FF88 !important; } - /* Console Tables */ + /* Console Tables - Light */ body.trading-console .data-table th { + background: var(--bg); + color: var(--text-muted); + } + /* Console Tables - Dark */ + [data-theme="dark"] body.trading-console .data-table th { background: rgba(0,255,136,0.03); color: rgba(0,255,136,0.6); font-family: 'SF Mono', 'Fira Code', 'Consolas', monospace; } - body.trading-console .data-table td { + [data-theme="dark"] body.trading-console .data-table td { font-family: 'SF Mono', 'Fira Code', 'Consolas', monospace; font-size: 12px; border-bottom-color: rgba(0,255,136,0.06); } - body.trading-console .data-table tr:hover td { + [data-theme="dark"] body.trading-console .data-table tr:hover td { background: rgba(0,255,136,0.05); } - /* Console Buttons */ - body.trading-console .preset-btn { + /* Console Buttons - Dark only overrides */ + [data-theme="dark"] body.trading-console .preset-btn { background: rgba(255,255,255,0.03); color: var(--text-secondary); border-color: rgba(0,255,136,0.1); } - body.trading-console .preset-btn:hover { + [data-theme="dark"] body.trading-console .preset-btn:hover { border-color: #00FF88; color: #00FF88; } - body.trading-console .preset-btn.active { + [data-theme="dark"] body.trading-console .preset-btn.active { background: rgba(0,255,136,0.15); color: #00FF88; border-color: rgba(0,255,136,0.3); } - body.trading-console .gran-btn { + [data-theme="dark"] body.trading-console .gran-btn { background: rgba(255,255,255,0.03); color: var(--text-secondary); border-color: rgba(0,255,136,0.1); } - body.trading-console .gran-btn:hover { + [data-theme="dark"] body.trading-console .gran-btn:hover { border-color: #F9A825; color: #F9A825; } - body.trading-console .gran-btn.active { + [data-theme="dark"] body.trading-console .gran-btn.active { background: rgba(249,168,37,0.15); color: #F9A825; border-color: rgba(249,168,37,0.3); } - body.trading-console .date-inputs input[type="date"] { + [data-theme="dark"] body.trading-console .date-inputs input[type="date"] { background: rgba(255,255,255,0.03); color: var(--text); border-color: rgba(0,255,136,0.1); } - /* Console Ranks */ - body.trading-console .rank-1 { background: rgba(249,168,37,0.15); color: #F9A825; } - body.trading-console .rank-2 { background: rgba(255,255,255,0.08); color: #9AA0A6; } - body.trading-console .rank-3 { background: rgba(216,67,21,0.15); color: #FF7043; } - body.trading-console .rank-default { background: rgba(255,255,255,0.05); } + /* Console Ranks - Dark */ + [data-theme="dark"] body.trading-console .rank-1 { background: rgba(249,168,37,0.15); color: #F9A825; } + [data-theme="dark"] body.trading-console .rank-2 { background: rgba(255,255,255,0.08); color: #9AA0A6; } + [data-theme="dark"] body.trading-console .rank-3 { background: rgba(216,67,21,0.15); color: #FF7043; } + [data-theme="dark"] body.trading-console .rank-default { background: rgba(255,255,255,0.05); } - /* Console Loading */ - body.trading-console .loading-overlay { background: rgba(10,15,24,0.85); } + /* Console Loading - Dark */ + [data-theme="dark"] body.trading-console .loading-overlay { background: rgba(10,15,24,0.85); } - /* Console Footer */ - body.trading-console .app-footer { + /* Console Footer - Dark */ + [data-theme="dark"] body.trading-console .app-footer { background: #0A0F18; border-top-color: rgba(0,255,136,0.1); color: var(--text-muted); } - /* Console Scrollbars */ - body.trading-console ::-webkit-scrollbar { width: 6px; height: 6px; } - body.trading-console ::-webkit-scrollbar-track { background: rgba(0,0,0,0.2); } - body.trading-console ::-webkit-scrollbar-thumb { background: rgba(0,255,136,0.2); border-radius: 3px; } - body.trading-console ::-webkit-scrollbar-thumb:hover { background: rgba(0,255,136,0.35); } + /* Console Scrollbars - Dark */ + [data-theme="dark"] body.trading-console ::-webkit-scrollbar { width: 6px; height: 6px; } + [data-theme="dark"] body.trading-console ::-webkit-scrollbar-track { background: rgba(0,0,0,0.2); } + [data-theme="dark"] body.trading-console ::-webkit-scrollbar-thumb { background: rgba(0,255,136,0.2); border-radius: 3px; } + [data-theme="dark"] body.trading-console ::-webkit-scrollbar-thumb:hover { background: rgba(0,255,136,0.35); } - /* === Floating Console Navigation === */ + /* === Folder-Tab Navigation (right edge) === */ .console-nav { position: fixed; right: 0; top: 50%; transform: translateY(-50%); - z-index: 1000; display: flex; flex-direction: column; gap: 2px; - padding: 6px 4px 6px 8px; - background: rgba(15,25,35,0.92); - backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px); - border: 1px solid rgba(0,255,136,0.15); border-right: none; - border-radius: 12px 0 0 12px; - box-shadow: -4px 0 20px rgba(0,0,0,0.4); + z-index: 1000; display: flex; flex-direction: column; gap: 0; + padding: 0; + background: none; } .console-nav-btn { - display: flex; align-items: center; gap: 6px; - padding: 8px 10px; background: transparent; - border: 1px solid transparent; border-radius: 8px; - color: rgba(255,255,255,0.4); font-size: 11px; font-weight: 600; - font-family: 'SF Mono', 'Fira Code', 'Consolas', monospace; - cursor: pointer; transition: all 0.2s; white-space: nowrap; - text-decoration: none; + display: flex; flex-direction: column; align-items: center; justify-content: center; + gap: 6px; + width: 38px; + padding: 14px 0; + background: var(--card); + border: 1px solid var(--border); + border-right: none; + border-radius: 8px 0 0 8px; + margin-bottom: -1px; + color: var(--text-muted); + font-size: 11px; font-weight: 600; + cursor: pointer; transition: all 0.2s ease; + text-decoration: none; position: relative; + box-shadow: -2px 0 6px rgba(0,0,0,0.04); } .console-nav-btn:hover { - color: rgba(255,255,255,0.7); background: rgba(0,255,136,0.05); + color: var(--text); + background: var(--bg); + width: 42px; } .console-nav-btn.active { - color: #00FF88; background: rgba(0,255,136,0.08); - border-color: rgba(0,255,136,0.2); + color: var(--tc-accent); + background: var(--bg); + width: 44px; + border-color: var(--tc-accent); + border-right: 1px solid var(--bg); + z-index: 2; + box-shadow: -3px 0 10px rgba(0,0,0,0.08); + } + /* Dark theme tabs */ + [data-theme="dark"] .console-nav-btn { + background: #161B22; + border-color: rgba(0,255,136,0.1); + color: rgba(255,255,255,0.35); + box-shadow: -2px 0 8px rgba(0,0,0,0.3); + } + [data-theme="dark"] .console-nav-btn:hover { + background: #1A2332; + color: rgba(255,255,255,0.7); + } + [data-theme="dark"] .console-nav-btn.active { + background: #0D1117; + color: #00FF88; + border-color: rgba(0,255,136,0.3); + border-right-color: #0D1117; + box-shadow: -3px 0 12px rgba(0,0,0,0.4); text-shadow: 0 0 8px rgba(0,255,136,0.4); } + .console-nav-btn .nav-icon { - font-size: 14px; width: 20px; text-align: center; flex-shrink: 0; + font-size: 15px; line-height: 1; flex-shrink: 0; } .console-nav-btn .nav-label { - font-size: 10px; letter-spacing: 0.5px; text-transform: uppercase; + writing-mode: vertical-rl; + text-orientation: mixed; + font-size: 9px; letter-spacing: 1px; text-transform: uppercase; + font-weight: 700; + white-space: nowrap; } - @media (min-width: 769px) { - body.trading-console .app-container { padding-right: 70px; } - } - @media (max-width: 900px) { - .console-nav-btn .nav-label { display: none; } - .console-nav-btn { padding: 8px; } - .console-nav { padding: 6px 4px 6px 6px; } - } - @media (max-width: 768px) { - .console-nav-btn .nav-label { display: none; } - .console-nav-btn { padding: 10px; min-height: 40px; min-width: 40px; justify-content: center; } - .console-nav-btn .nav-icon { font-size: 16px; } - .console-nav { gap: 1px; padding: 4px 3px 4px 5px; } - } - @media (max-width: 480px) { - .console-nav { - top: 50%; transform: translateY(-50%); right: 0; - flex-direction: column; - padding: 3px 2px 3px 4px; - } - .console-nav-btn { padding: 7px; min-height: 34px; min-width: 34px; } - .console-nav-btn .nav-icon { font-size: 13px; } + [data-theme="dark"] .console-nav-btn .nav-label { + font-family: 'SF Mono', 'Fira Code', 'Consolas', monospace; } - /* Trading Terminal - Live Rates */ + @media (min-width: 769px) { + body.trading-console .app-container { padding-right: 50px; } + } + /* Tablet: icon + short label */ + @media (max-width: 900px) { + .console-nav-btn { width: 34px; padding: 10px 0; } + .console-nav-btn .nav-label { font-size: 8px; } + .console-nav-btn:hover { width: 38px; } + .console-nav-btn.active { width: 40px; } + } + /* Mobile: icon only */ + @media (max-width: 768px) { + .console-nav-btn .nav-label { display: none; } + .console-nav-btn { width: 36px; padding: 12px 0; } + .console-nav-btn .nav-icon { font-size: 16px; } + .console-nav-btn:hover { width: 40px; } + .console-nav-btn.active { width: 42px; } + } + @media (max-width: 480px) { + .console-nav-btn { width: 30px; padding: 10px 0; } + .console-nav-btn .nav-icon { font-size: 13px; } + .console-nav-btn:hover { width: 34px; } + .console-nav-btn.active { width: 36px; } + } + + /* Trading Terminal - Live Rates: Light */ .trading-terminal { - background: linear-gradient(135deg, #0F1923 0%, #1A2332 50%, #0D1B2A 100%); - border-top: 1px solid rgba(0,255,136,0.15); - border-bottom: 1px solid rgba(0,255,136,0.15); + background: linear-gradient(135deg, #F8F9FB 0%, #EEF1F5 50%, #F0F2F5 100%); + border-top: 1px solid var(--border); + border-bottom: 1px solid var(--border); padding: 16px 40px; position: relative; overflow: hidden; } .trading-terminal::before { content: ''; position: absolute; top: 0; left: 0; right: 0; height: 1px; - background: linear-gradient(90deg, transparent, rgba(0,255,136,0.3), transparent); + background: linear-gradient(90deg, transparent, rgba(30,142,62,0.2), transparent); } .trading-terminal::after { content: ''; position: absolute; bottom: 0; left: 0; right: 0; height: 1px; + background: linear-gradient(90deg, transparent, rgba(30,142,62,0.2), transparent); + } + /* Trading Terminal - Dark */ + [data-theme="dark"] .trading-terminal { + background: linear-gradient(135deg, #0F1923 0%, #1A2332 50%, #0D1B2A 100%); + border-top: 1px solid rgba(0,255,136,0.15); + border-bottom: 1px solid rgba(0,255,136,0.15); + } + [data-theme="dark"] .trading-terminal::before { + background: linear-gradient(90deg, transparent, rgba(0,255,136,0.3), transparent); + } + [data-theme="dark"] .trading-terminal::after { background: linear-gradient(90deg, transparent, rgba(0,255,136,0.3), transparent); } .live-rate-bar { @@ -213,26 +306,37 @@ function buildAdminBIHTML(user) { max-width: 1600px; margin: 0 auto; } .terminal-title { - font-size: 10px; font-weight: 700; color: rgba(0,255,136,0.6); + font-size: 10px; font-weight: 700; color: var(--tc-accent); text-transform: uppercase; letter-spacing: 2px; margin-right: 8px; + opacity: 0.7; + } + [data-theme="dark"] .terminal-title { font-family: 'SF Mono', 'Fira Code', 'Consolas', monospace; } .live-rate-dot { - width: 7px; height: 7px; border-radius: 50%; background: #00FF88; + width: 7px; height: 7px; border-radius: 50%; background: var(--tc-accent); display: inline-block; animation: blink 1.5s infinite; - box-shadow: 0 0 6px rgba(0,255,136,0.6); + box-shadow: 0 0 6px var(--tc-accent-bg); } @keyframes blink { 0%, 100% { opacity: 1; } 50% { opacity: 0.2; } } .rate-pair-group { display: flex; align-items: center; gap: 8px; - background: rgba(255,255,255,0.03); - border: 1px solid rgba(255,255,255,0.06); + background: var(--card); + border: 1px solid var(--border); border-radius: 8px; padding: 8px 14px; } + [data-theme="dark"] .rate-pair-group { + background: rgba(255,255,255,0.03); + border-color: rgba(255,255,255,0.06); + } .rate-pair-label { - font-size: 11px; font-weight: 800; color: rgba(255,255,255,0.35); + font-size: 11px; font-weight: 800; color: var(--text-muted); text-transform: uppercase; letter-spacing: 1px; writing-mode: vertical-rl; - text-orientation: mixed; font-family: 'SF Mono', 'Fira Code', 'Consolas', monospace; + text-orientation: mixed; + } + [data-theme="dark"] .rate-pair-label { + font-family: 'SF Mono', 'Fira Code', 'Consolas', monospace; + color: rgba(255,255,255,0.35); } .live-rate-btn { display: flex; flex-direction: column; align-items: center; gap: 1px; @@ -241,18 +345,28 @@ function buildAdminBIHTML(user) { transition: all 0.25s; min-width: 150px; justify-content: center; position: relative; } .rate-flags { font-size: 9px; opacity: 0.5; letter-spacing: 1px; line-height: 1; } - .live-rate-btn.compra { background: rgba(0,255,136,0.08); color: #00FF88; border-color: rgba(0,255,136,0.2); } - .live-rate-btn.compra:hover { background: rgba(0,255,136,0.14); } - .live-rate-btn.venda { background: rgba(255,68,68,0.08); color: #FF4444; border-color: rgba(255,68,68,0.2); } - .live-rate-btn.venda:hover { background: rgba(255,68,68,0.14); } + .live-rate-btn.compra { background: var(--green-bg); color: var(--green); border-color: rgba(30,142,62,0.2); } + .live-rate-btn.compra:hover { background: rgba(30,142,62,0.12); } + .live-rate-btn.venda { background: var(--red-bg); color: var(--red); border-color: rgba(217,48,37,0.2); } + .live-rate-btn.venda:hover { background: rgba(217,48,37,0.12); } + [data-theme="dark"] .live-rate-btn.compra { background: rgba(0,255,136,0.08); color: #00FF88; border-color: rgba(0,255,136,0.2); } + [data-theme="dark"] .live-rate-btn.compra:hover { background: rgba(0,255,136,0.14); } + [data-theme="dark"] .live-rate-btn.venda { background: rgba(255,68,68,0.08); color: #FF4444; border-color: rgba(255,68,68,0.2); } + [data-theme="dark"] .live-rate-btn.venda:hover { background: rgba(255,68,68,0.14); } .live-rate-btn .rate-value { font-size: 22px; font-weight: 800; letter-spacing: -0.5px; + font-variant-numeric: tabular-nums; + } + [data-theme="dark"] .live-rate-btn .rate-value { font-family: 'SF Mono', 'Fira Code', 'Consolas', monospace; - font-variant-numeric: tabular-nums; text-shadow: 0 0 12px currentColor; + text-shadow: 0 0 12px currentColor; } .live-rate-btn .rate-type { font-size: 9px; text-transform: uppercase; letter-spacing: 1.5px; - opacity: 0.5; font-family: 'SF Mono', 'Fira Code', 'Consolas', monospace; + opacity: 0.5; + } + [data-theme="dark"] .live-rate-btn .rate-type { + font-family: 'SF Mono', 'Fira Code', 'Consolas', monospace; } .live-rate-btn.pulse .rate-value { animation: ratePulse 0.4s ease; } @keyframes ratePulse { @@ -261,8 +375,11 @@ function buildAdminBIHTML(user) { 100% { transform: scale(1); text-shadow: 0 0 12px currentColor; } } .live-rate-time { - font-size: 10px; color: rgba(255,255,255,0.25); - font-family: 'SF Mono', 'Fira Code', 'Consolas', monospace; letter-spacing: 0.5px; + font-size: 10px; color: var(--text-muted); letter-spacing: 0.5px; + } + [data-theme="dark"] .live-rate-time { + color: rgba(255,255,255,0.25); + font-family: 'SF Mono', 'Fira Code', 'Consolas', monospace; } /* Filter Bar */ @@ -299,9 +416,10 @@ function buildAdminBIHTML(user) { display: grid; grid-template-columns: repeat(5, 1fr); gap: 16px; margin-bottom: 28px; } .hero-card { - background: var(--card); border-radius: 16px; padding: 20px 24px; + background: var(--card); border-radius: 16px; padding: 20px 18px; border: 1px solid var(--border); box-shadow: 0 2px 8px rgba(0,0,0,0.04); position: relative; overflow: hidden; + min-width: 0; } .hero-card::before { content: ''; position: absolute; top: 0; left: 0; right: 0; height: 3px; @@ -316,8 +434,10 @@ function buildAdminBIHTML(user) { text-transform: uppercase; letter-spacing: 0.5px; margin-bottom: 8px; } .hero-value { - font-size: 28px; font-weight: 800; color: var(--text); margin-bottom: 4px; + font-size: clamp(14px, 1.6vw, 26px); font-weight: 800; color: var(--text); margin-bottom: 4px; font-variant-numeric: tabular-nums; + word-break: break-word; overflow-wrap: break-word; + min-width: 0; line-height: 1.2; } .hero-badge { display: inline-block; font-size: 11px; font-weight: 700; padding: 3px 10px; @@ -379,9 +499,9 @@ function buildAdminBIHTML(user) { width: 32px; height: 32px; border-radius: 50%; display: inline-flex; align-items: center; justify-content: center; font-weight: 800; font-size: 12px; } - .rank-1 { background: #FFF8E1; color: #F9A825; } - .rank-2 { background: #F5F5F5; color: #757575; } - .rank-3 { background: #FBE9E7; color: #D84315; } + .rank-1 { background: rgba(249,168,37,0.12); color: #F9A825; } + .rank-2 { background: rgba(0,0,0,0.04); color: #757575; } + .rank-3 { background: rgba(216,67,21,0.1); color: #D84315; } .rank-default { background: var(--bg); color: var(--text-muted); } /* Gauge / Metric Cards */ @@ -411,6 +531,20 @@ function buildAdminBIHTML(user) { .gauge-bar-fill.blue { background: linear-gradient(90deg, var(--blue), #42A5F5); } .gauge-label { font-size: 12px; color: var(--text-muted); } + /* Netting Gran Buttons */ + .netting-gran-btn { + width: 32px; height: 32px; border-radius: 50%; + border: 1px solid var(--border); background: var(--bg); + font-size: 12px; font-weight: 700; cursor: pointer; + color: var(--text-secondary); transition: all 0.15s; font-family: inherit; + display: inline-flex; align-items: center; justify-content: center; + } + .netting-gran-btn:hover { border-color: var(--tc-accent); color: var(--tc-accent); } + .netting-gran-btn.active { background: var(--tc-accent); color: white; border-color: var(--tc-accent); } + [data-theme="dark"] .netting-gran-btn { background: rgba(255,255,255,0.03); color: var(--text-secondary); border-color: rgba(0,255,136,0.1); } + [data-theme="dark"] .netting-gran-btn:hover { border-color: #00FF88; color: #00FF88; } + [data-theme="dark"] .netting-gran-btn.active { background: rgba(0,255,136,0.15); color: #00FF88; border-color: rgba(0,255,136,0.3); } + /* Netting Card */ .netting-row { display: flex; justify-content: space-between; align-items: center; @@ -445,6 +579,46 @@ function buildAdminBIHTML(user) { .hero-grid-4 { grid-template-columns: repeat(4, 1fr); } .hero-card[style*="--top-color"]::before { background: var(--top-color); } + /* Cohort Heatmap */ + .cohort-table { width: 100%; border-collapse: collapse; font-size: 11px; overflow-x: auto; display: block; } + .cohort-table th, .cohort-table td { padding: 6px 8px; text-align: center; white-space: nowrap; border: 1px solid var(--border); min-width: 48px; } + .cohort-table th { font-weight: 700; font-size: 10px; text-transform: uppercase; color: var(--text-muted); background: var(--bg); position: sticky; top: 0; } + .cohort-table th:first-child, .cohort-table td:first-child { text-align: left; position: sticky; left: 0; z-index: 1; background: var(--card); min-width: 90px; } + .cohort-table td:nth-child(2) { font-weight: 700; color: var(--text); } + .cohort-cell { font-weight: 600; font-variant-numeric: tabular-nums; border-radius: 2px; } + [data-theme="dark"] .cohort-table th:first-child, [data-theme="dark"] .cohort-table td:first-child { background: rgba(255,255,255,0.03); } + + /* Waterfall */ + .waterfall-grid { display: grid; grid-template-columns: repeat(5, 1fr); gap: 12px; } + .waterfall-item { text-align: center; padding: 16px 8px; border-radius: 12px; border: 1px solid var(--border); background: var(--card); } + .waterfall-item .wf-label { font-size: 10px; font-weight: 700; text-transform: uppercase; color: var(--text-muted); letter-spacing: 0.5px; margin-bottom: 8px; } + .waterfall-item .wf-count { font-size: 28px; font-weight: 800; line-height: 1; margin-bottom: 4px; font-variant-numeric: tabular-nums; } + .waterfall-item .wf-revenue { font-size: 13px; font-weight: 700; font-variant-numeric: tabular-nums; } + .waterfall-item.wf-new { border-color: var(--blue); } + .waterfall-item.wf-new .wf-count { color: var(--blue); } + .waterfall-item.wf-expansion { border-color: var(--green); } + .waterfall-item.wf-expansion .wf-count { color: var(--green); } + .waterfall-item.wf-stable .wf-count { color: var(--text-secondary); } + .waterfall-item.wf-contraction { border-color: var(--orange); } + .waterfall-item.wf-contraction .wf-count { color: var(--orange); } + .waterfall-item.wf-churned { border-color: var(--red); } + .waterfall-item.wf-churned .wf-count { color: var(--red); } + [data-theme="dark"] .waterfall-item { background: rgba(255,255,255,0.02); } + [data-theme="dark"] .waterfall-item .wf-count { font-family: 'SF Mono','Fira Code','Consolas',monospace; text-shadow: 0 0 8px currentColor; } + + /* Cross-sell & Maturity */ + .segment-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; margin-bottom: 28px; } + .segment-card { border-radius: 16px; padding: 24px; background: var(--card); border: 1px solid var(--border); } + .segment-card h3 { font-size: 14px; font-weight: 700; color: var(--text); margin-bottom: 16px; display: flex; align-items: center; gap: 8px; } + .segment-bars { display: flex; flex-direction: column; gap: 10px; } + .seg-row { display: flex; align-items: center; gap: 12px; } + .seg-label { font-size: 12px; font-weight: 600; color: var(--text-secondary); min-width: 110px; } + .seg-bar-wrap { flex: 1; height: 24px; border-radius: 6px; background: var(--bg); overflow: hidden; position: relative; } + .seg-bar-fill { height: 100%; border-radius: 6px; transition: width 0.6s; display: flex; align-items: center; padding: 0 8px; min-width: 0; } + .seg-bar-text { font-size: 10px; font-weight: 700; color: white; white-space: nowrap; } + .seg-count { font-size: 13px; font-weight: 700; min-width: 50px; text-align: right; color: var(--text); font-variant-numeric: tabular-nums; } + [data-theme="dark"] .segment-card { background: rgba(255,255,255,0.03); border-color: rgba(0,255,136,0.1); } + /* Loading */ .loading-overlay { position: absolute; inset: 0; background: rgba(255,255,255,0.8); @@ -452,6 +626,13 @@ function buildAdminBIHTML(user) { border-radius: 16px; z-index: 10; font-size: 13px; color: var(--text-muted); } + /* Responsive - Small Desktop */ + @media (max-width: 1200px) { + .hero-grid { grid-template-columns: repeat(3, 1fr); } + .hero-grid-4 { grid-template-columns: repeat(2, 1fr); } + .hero-value { font-size: clamp(15px, 2.4vw, 22px); } + } + /* Responsive - Tablet */ @media (max-width: 900px) { .trading-terminal { padding: 12px 16px; } @@ -461,8 +642,6 @@ function buildAdminBIHTML(user) { .live-rate-btn { min-width: 100px; padding: 6px 12px; } .live-rate-btn .rate-value { font-size: 16px; } .rate-separator { display: none; } - .hero-grid { grid-template-columns: repeat(3, 1fr); } - .hero-grid-4 { grid-template-columns: repeat(2, 1fr); } .charts-row { grid-template-columns: 1fr; } .charts-row.equal { grid-template-columns: 1fr; } .charts-row.triple { grid-template-columns: 1fr 1fr; } @@ -539,6 +718,12 @@ function buildAdminBIHTML(user) { /* Netting */ .netting-label { font-size: 12px; } .netting-value { font-size: 14px; } + + /* Strategic sections */ + .waterfall-grid { grid-template-columns: repeat(3, 1fr); } + .segment-grid { grid-template-columns: 1fr; } + .cohort-table { font-size: 10px; } + .cohort-table th, .cohort-table td { padding: 4px 6px; min-width: 40px; } } /* Responsive - Small Mobile */ @@ -585,6 +770,11 @@ function buildAdminBIHTML(user) { /* Section titles */ .section-title { font-size: 12px; letter-spacing: 0.5px; } .section-title .icon { width: 24px; height: 24px; font-size: 12px; } + + /* Strategic */ + .waterfall-grid { grid-template-columns: repeat(2, 1fr); } + .waterfall-item .wf-count { font-size: 22px; } + .seg-label { min-width: 80px; font-size: 11px; } } /* Dark Mode overrides */ @@ -655,7 +845,7 @@ ${buildHeader({ role: role, userName: user.nome, activePage: 'bi' })}
-
Receita de Spread
+
Receita USD (Spread)
--
--
vs periodo anterior
@@ -707,7 +897,10 @@ ${buildHeader({ role: role, userName: user.nome, activePage: 'bi' })}
-

Top 10 Clientes por Volume

+

Top 10 Clientes por Volume + +

@@ -716,14 +909,25 @@ ${buildHeader({ role: role, userName: user.nome, activePage: 'bi' })}
--%
-- de -- clientes retidos
+
+ +0 novos + -0 perdidos +
-

- Clientes em Risco (30+ dias sem operar) +

+ Clientes em Risco

+
+ Inativos ha: + + + + +
- - + +
ClienteUltima OpVolume USD
Carregando...
ClienteUltima OpDiasVolume USD
Carregando...
@@ -734,13 +938,41 @@ ${buildHeader({ role: role, userName: user.nome, activePage: 'bi' })} Operacional
-
+

Volume por Corredor

+
+

Ranking Agentes

+
+ + + +
#AgenteVolumeOpsSpread R$Clientes
Carregando...
+
+
+
+ + +
+ + Netting & Balanco +
+
+
+
+

Fluxo Entrada vs Saida

+
+ + + +
+
+
+
-

Netting & Balanco

+

Resumo Netting

Saida (BRL→USD) @@ -761,15 +993,6 @@ ${buildHeader({ role: role, userName: user.nome, activePage: 'bi' })}
-
-

Ranking Agentes

-
- - - -
#AgenteVolumeOpsSpread R$Clientes
Carregando...
-
-
@@ -832,6 +1055,83 @@ ${buildHeader({ role: role, userName: user.nome, activePage: 'bi' })}
+ +
+ 📉 + Revenue Expansion & Contraction +
+
+
+
Novos
+
--
+
--
+
+
+
Expansao
+
--
+
--
+
+
+
Estavel
+
--
+
--
+
+
+
Contracao
+
--
+
--
+
+
+
Churned
+
--
+
--
+
+
+ +
+
+

Waterfall de Receita

+
+
+
+

Distribuicao por Segmento

+
+
+
+ + +
+ 🎯 + Segmentacao Estrategica +
+
+
+

Cross-sell CambioPay vs Checkout

+
+
+
+
+

Maturidade de Clientes

+
+
+
+
+ + +
+ 📊 + Retencao por Cohort +
+
+

Cohort Retention Heatmap ultimos 12 meses

+
+ + + +
CohortClientes
Carregando...
+
+
+ ${buildFooter()} @@ -853,18 +1153,23 @@ ${buildFooter()} \u2699Ops + + \u2696Netting + \uD83D\uDCB0P&L + + \uD83D\uDCC9Exp + + + \uD83C\uDFAFSeg + + + \uD83D\uDCCACohort + - -