Created
December 28, 2025 17:45
-
-
Save Jcbertorello/107ca00197e6cdd914c4d70af56c9d67 to your computer and use it in GitHub Desktop.
Dashboard Ajonjolí - 2025-12-28 17:45
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <!DOCTYPE html> | |
| <html lang="es"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Ventas de Confitería - Octubre 2025 - Cinexo</title> | |
| <!-- Google Fonts para tipografía premium --> | |
| <link rel="preconnect" href="https://fonts.googleapis.com"> | |
| <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> | |
| <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet"> | |
| <!-- Chart.js + Plugin Datalabels --> | |
| <script src="https://cdn.jsdelivr.net/npm/chart.js"></script> | |
| <script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-datalabels@2"></script> | |
| <!-- html2pdf.js for PDF export --> | |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/html2pdf.js/0.10.1/html2pdf.bundle.min.js"></script> | |
| <style> | |
| /* === VARIABLES === */ | |
| :root { | |
| --color-primary: #60B99A; | |
| --color-primary-light: #7FCDB2; | |
| --color-primary-dark: #4A9D80; | |
| --color-primary-glow: rgba(96, 185, 154, 0.3); | |
| --color-secondary: #1F3B4D; | |
| --color-secondary-light: #2D5066; | |
| --color-boleteria: #60B99A; | |
| --color-candy: #FFB74D; | |
| --color-candy-dark: #F59E0B; | |
| --color-success: #10B981; | |
| --color-success-light: #D1FAE5; | |
| --color-danger: #EF4444; | |
| --color-danger-light: #FEE2E2; | |
| --color-warning: #F59E0B; | |
| --color-warning-light: #FEF3C7; | |
| --ocupacion-alta: #10B981; | |
| --ocupacion-media: #F59E0B; | |
| --ocupacion-baja: #EF4444; | |
| --bg-primary: #F8FAFC; | |
| --bg-card: #FFFFFF; | |
| --bg-hover: #F1F5F9; | |
| --border-light: #E2E8F0; | |
| --border-medium: #CBD5E1; | |
| --text-primary: #1E293B; | |
| --text-secondary: #475569; | |
| --text-muted: #64748B; | |
| --text-light: #94A3B8; | |
| --shadow-sm: 0 1px 2px rgba(0,0,0,0.05); | |
| --shadow-md: 0 4px 6px -1px rgba(0,0,0,0.1), 0 2px 4px -1px rgba(0,0,0,0.06); | |
| --shadow-lg: 0 10px 15px -3px rgba(0,0,0,0.1), 0 4px 6px -2px rgba(0,0,0,0.05); | |
| --shadow-xl: 0 20px 25px -5px rgba(0,0,0,0.1), 0 10px 10px -5px rgba(0,0,0,0.04); | |
| --shadow-glow: 0 0 20px var(--color-primary-glow); | |
| } | |
| /* === RESET === */ | |
| * { margin: 0; padding: 0; box-sizing: border-box; } | |
| body { | |
| font-family: 'Inter', system-ui, sans-serif; | |
| background: linear-gradient(180deg, var(--bg-primary) 0%, #EFF6FF 100%); | |
| color: var(--text-primary); | |
| line-height: 1.6; | |
| min-height: 100vh; | |
| } | |
| /* === DASHBOARD CONTAINER === */ | |
| .dashboard { | |
| max-width: 1400px; | |
| margin: 0 auto; | |
| padding: 32px 24px; | |
| } | |
| /* === HEADER PREMIUM === */ | |
| .header { | |
| background: linear-gradient(135deg, var(--color-secondary) 0%, var(--color-secondary-light) 100%); | |
| border-radius: 20px; | |
| padding: 28px 32px; | |
| margin-bottom: 24px; | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| box-shadow: var(--shadow-lg); | |
| position: relative; | |
| overflow: hidden; | |
| } | |
| .header::before { | |
| content: ''; | |
| position: absolute; | |
| top: -50%; | |
| right: -20%; | |
| width: 400px; | |
| height: 400px; | |
| background: radial-gradient(circle, rgba(255, 183, 77, 0.15) 0%, transparent 70%); | |
| pointer-events: none; | |
| } | |
| .header-left { | |
| display: flex; | |
| align-items: center; | |
| gap: 16px; | |
| z-index: 1; | |
| } | |
| .logo { | |
| width: 56px; | |
| height: 56px; | |
| background: linear-gradient(135deg, var(--color-candy) 0%, var(--color-candy-dark) 100%); | |
| border-radius: 14px; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| font-size: 1.5rem; | |
| font-weight: 800; | |
| color: white; | |
| box-shadow: 0 8px 20px rgba(255, 183, 77, 0.4); | |
| } | |
| .header-title { | |
| color: white; | |
| font-size: 1.75rem; | |
| font-weight: 800; | |
| letter-spacing: -0.02em; | |
| } | |
| .header-subtitle { | |
| color: rgba(255,255,255,0.8); | |
| font-size: 0.9rem; | |
| margin-top: 2px; | |
| } | |
| .header-actions { | |
| display: flex; | |
| gap: 12px; | |
| z-index: 1; | |
| } | |
| .btn { | |
| display: inline-flex; | |
| align-items: center; | |
| gap: 8px; | |
| padding: 12px 20px; | |
| border-radius: 10px; | |
| font-weight: 600; | |
| font-size: 0.9rem; | |
| cursor: pointer; | |
| border: none; | |
| transition: all 0.2s ease; | |
| } | |
| .btn-primary { | |
| background: linear-gradient(135deg, var(--color-candy), var(--color-candy-dark)); | |
| color: white; | |
| box-shadow: 0 4px 12px rgba(255, 183, 77, 0.4); | |
| } | |
| .btn-primary:hover { | |
| transform: translateY(-2px); | |
| box-shadow: 0 6px 20px rgba(255, 183, 77, 0.5); | |
| } | |
| .btn-secondary { | |
| background: rgba(255,255,255,0.15); | |
| color: white; | |
| border: 1px solid rgba(255,255,255,0.3); | |
| backdrop-filter: blur(10px); | |
| } | |
| .btn-secondary:hover { background: rgba(255,255,255,0.25); } | |
| /* === INFO BAR === */ | |
| .info-bar { | |
| background: var(--bg-card); | |
| border-radius: 12px; | |
| padding: 16px 24px; | |
| margin-bottom: 24px; | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| box-shadow: var(--shadow-md); | |
| border-left: 4px solid var(--color-candy); | |
| } | |
| .info-item { | |
| display: flex; | |
| align-items: center; | |
| gap: 8px; | |
| font-size: 0.875rem; | |
| color: var(--text-muted); | |
| } | |
| .info-item strong { color: var(--text-primary); font-weight: 600; } | |
| .info-item svg { width: 16px; height: 16px; } | |
| /* === KPI CARDS PREMIUM === */ | |
| .kpi-grid { | |
| display: grid; | |
| grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); | |
| gap: 24px; | |
| margin-bottom: 32px; | |
| } | |
| .kpi-card { | |
| background: var(--bg-card); | |
| border-radius: 16px; | |
| padding: 24px; | |
| position: relative; | |
| overflow: hidden; | |
| box-shadow: var(--shadow-md); | |
| transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); | |
| border: 1px solid var(--border-light); | |
| } | |
| .kpi-card::before { | |
| content: ''; | |
| position: absolute; | |
| top: 0; | |
| left: 0; | |
| right: 0; | |
| height: 4px; | |
| background: linear-gradient(90deg, var(--color-secondary), var(--color-secondary-light)); | |
| } | |
| .kpi-card.candy::before { background: linear-gradient(90deg, var(--color-candy), var(--color-candy-dark)); } | |
| .kpi-card:hover { | |
| transform: translateY(-4px); | |
| box-shadow: var(--shadow-xl); | |
| border-color: var(--color-candy); | |
| } | |
| .kpi-icon { | |
| width: 48px; | |
| height: 48px; | |
| border-radius: 12px; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| font-size: 1.5rem; | |
| margin-bottom: 16px; | |
| } | |
| .kpi-card.candy .kpi-icon { background: linear-gradient(135deg, rgba(255, 183, 77, 0.1), rgba(255, 183, 77, 0.2)); color: var(--color-candy-dark); } | |
| .kpi-card.profit .kpi-icon { background: linear-gradient(135deg, rgba(96, 185, 154, 0.1), rgba(96, 185, 154, 0.2)); color: var(--color-primary-dark); } | |
| .kpi-card.profit::before { background: linear-gradient(90deg, var(--color-primary), var(--color-primary-dark)); } | |
| .kpi-label { | |
| font-size: 0.7rem; | |
| font-weight: 700; | |
| text-transform: uppercase; | |
| letter-spacing: 0.1em; | |
| color: var(--text-muted); | |
| margin-bottom: 8px; | |
| } | |
| .kpi-value { | |
| font-size: 2rem; | |
| font-weight: 800; | |
| color: var(--text-primary); | |
| line-height: 1.1; | |
| margin-bottom: 8px; | |
| font-feature-settings: 'tnum'; | |
| } | |
| .kpi-trend { | |
| display: inline-flex; | |
| align-items: center; | |
| gap: 4px; | |
| padding: 4px 10px; | |
| border-radius: 20px; | |
| font-size: 0.75rem; | |
| font-weight: 700; | |
| } | |
| .kpi-trend.up { background: var(--color-success-light); color: var(--color-success); } | |
| .kpi-trend.down { background: var(--color-danger-light); color: var(--color-danger); } | |
| .kpi-comparison { position: absolute; bottom: 12px; right: 12px; width: 60px; height: 30px; } | |
| .kpi-sparkline { width: 100%; height: 100%; } | |
| /* === GRID & CARDS === */ | |
| .grid-container { | |
| display: grid; | |
| grid-template-columns: repeat(3, 1fr); | |
| gap: 24px; | |
| margin-bottom: 32px; | |
| } | |
| .grid-col-span-2 { grid-column: span 2; } | |
| .grid-col-span-3 { grid-column: span 3; } | |
| .card { | |
| background: var(--bg-card); | |
| border-radius: 16px; | |
| padding: 24px; | |
| box-shadow: var(--shadow-md); | |
| border: 1px solid var(--border-light); | |
| display: flex; | |
| flex-direction: column; | |
| } | |
| .card-header { | |
| margin-bottom: 20px; | |
| } | |
| .card-title { | |
| font-size: 1.2rem; | |
| font-weight: 700; | |
| color: var(--text-primary); | |
| } | |
| .card-subtitle { | |
| font-size: 0.85rem; | |
| color: var(--text-muted); | |
| margin-top: 4px; | |
| } | |
| .chart-container { | |
| position: relative; | |
| flex-grow: 1; | |
| min-height: 350px; /* Asegura altura mínima */ | |
| } | |
| /* === TABLE PREMIUM === */ | |
| .table-card { | |
| background: var(--bg-card); | |
| border-radius: 16px; | |
| overflow: hidden; | |
| box-shadow: var(--shadow-md); | |
| border: 1px solid var(--border-light); | |
| } | |
| .table-header { padding: 20px 24px; border-bottom: 1px solid var(--border-light); } | |
| .table-title { font-size: 1.1rem; font-weight: 700; color: var(--text-primary); } | |
| .table-subtitle { font-size: 0.8rem; color: var(--text-muted); margin-top: 2px; } | |
| .data-table { width: 100%; border-collapse: collapse; } | |
| .data-table thead { background: var(--bg-hover); } | |
| .data-table th { padding: 14px 20px; text-align: left; font-weight: 600; font-size: 0.7rem; text-transform: uppercase; letter-spacing: 0.08em; color: var(--text-muted); border-bottom: 2px solid var(--border-light); } | |
| .data-table td { padding: 16px 20px; border-bottom: 1px solid var(--border-light); font-size: 0.9rem; color: var(--text-primary); transition: background 0.15s ease; } | |
| .data-table tbody tr:hover td { background: rgba(255, 183, 77, 0.05); } | |
| .data-table tbody tr:last-child td { border-bottom: none; } | |
| .rank-medal { width: 28px; height: 28px; border-radius: 50%; display: inline-flex; align-items: center; justify-content: center; font-weight: 800; font-size: 0.8rem; color: white; } | |
| .rank-1 { background: linear-gradient(135deg, #FFD700, #FFA500); box-shadow: 0 2px 8px rgba(255, 215, 0, 0.4); } | |
| .rank-2 { background: linear-gradient(135deg, #E8E8E8, #B8B8B8); box-shadow: 0 2px 8px rgba(192, 192, 192, 0.4); } | |
| .rank-3 { background: linear-gradient(135deg, #CD7F32, #8B4513); box-shadow: 0 2px 8px rgba(205, 127, 50, 0.4); } | |
| .rank-other { background: var(--bg-hover); color: var(--text-muted); } | |
| .progress-cell { display: flex; align-items: center; gap: 12px; } | |
| .progress-bar-container { flex: 1; height: 8px; background: var(--border-light); border-radius: 4px; overflow: hidden; min-width: 80px; } | |
| .progress-bar-fill { height: 100%; border-radius: 4px; transition: width 1s ease-out; background: linear-gradient(90deg, var(--color-candy-dark), var(--color-candy)); } | |
| .progress-value { font-weight: 600; font-size: 0.85rem; min-width: 45px; text-align: right; } | |
| .value-currency { font-feature-settings: 'tnum'; font-weight: 600; } | |
| /* === FOOTER === */ | |
| .footer { text-align: center; padding: 24px; font-size: 0.85rem; color: var(--text-muted); margin-top: 32px; } | |
| .footer a { color: var(--color-secondary); font-weight: 600; text-decoration: none; } | |
| .footer a:hover { text-decoration: underline; } | |
| /* === PRINT/PDF === */ | |
| @media print { | |
| body { background: white !important; } | |
| .btn, .header-actions { display: none !important; } | |
| .dashboard { padding: 16px !important; max-width: 100% !important; } | |
| .header, .kpi-card, .card, .table-card { box-shadow: none !important; border: 1px solid #ddd !important; } | |
| .grid-container { grid-template-columns: 1fr 1fr; } | |
| .grid-col-span-2 { grid-column: span 2; } | |
| .grid-col-span-3 { grid-column: span 2; } | |
| } | |
| /* === RESPONSIVE === */ | |
| @media (max-width: 1200px) { | |
| .grid-container { grid-template-columns: 1fr 1fr; } | |
| .grid-col-span-3 { grid-column: span 2; } | |
| } | |
| @media (max-width: 992px) { | |
| .grid-container { grid-template-columns: 1fr; } | |
| .grid-col-span-2, .grid-col-span-3 { grid-column: span 1; } | |
| } | |
| @media (max-width: 768px) { | |
| .header { flex-direction: column; gap: 16px; text-align: center; } | |
| .info-bar { flex-direction: column; gap: 12px; align-items: flex-start; } | |
| .kpi-grid { grid-template-columns: 1fr; } | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="dashboard" id="dashboard-content"> | |
| <!-- Header Premium --> | |
| <header class="header"> | |
| <div class="header-left"> | |
| <div class="logo">CX</div> | |
| <div> | |
| <h1 class="header-title">Ventas de Confitería - Octubre 2025</h1> | |
| <p class="header-subtitle">Reporte de Performance para Cinexo</p> | |
| </div> | |
| </div> | |
| <div class="header-actions"> | |
| <button class="btn btn-primary" onclick="downloadPDF()"> | |
| <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16"> | |
| <path d="M.5 9.9a.5.5 0 0 1 .5.5v2.5a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-2.5a.5.5 0 0 1 1 0v2.5a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2v-2.5a.5.5 0 0 1 .5-.5z"/> | |
| <path d="M7.646 11.854a.5.5 0 0 0 .708 0l3-3a.5.5 0 0 0-.708-.708L8.5 10.293V1.5a.5.5 0 0 0-1 0v8.793L5.354 8.146a.5.5 0 1 0-.708.708l3 3z"/> | |
| </svg> | |
| Descargar PDF | |
| </button> | |
| </div> | |
| </header> | |
| <!-- Info Bar --> | |
| <div class="info-bar"> | |
| <div class="info-item"> | |
| <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"> | |
| <path stroke-linecap="round" stroke-linejoin="round" d="M6.75 3v2.25M17.25 3v2.25M3 18.75V7.5a2.25 2.25 0 0 1 2.25-2.25h13.5A2.25 2.25 0 0 1 21 7.5v11.25m-18 0A2.25 2.25 0 0 0 5.25 21h13.5A2.25 2.25 0 0 0 21 18.75m-18 0h18M12 11.25h.008v.008H12v-.008Z" /> | |
| </svg> | |
| <span>Período del reporte: <strong>01/10/2025 - 31/10/2025</strong></span> | |
| </div> | |
| <div class="info-item"> | |
| <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"> | |
| <path stroke-linecap="round" stroke-linejoin="round" d="M12 6v6h4.5m4.5 0a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z" /> | |
| </svg> | |
| <span>Última actualización: <strong>31/10/2025 23:59</strong></span> | |
| </div> | |
| </div> | |
| <!-- KPI Grid --> | |
| <div class="kpi-grid"> | |
| <div class="kpi-card candy"> | |
| <div class="kpi-icon">💰</div> | |
| <div class="kpi-label">Ingresos Totales</div> | |
| <div class="kpi-value" id="kpi-ingresos"></div> | |
| <div class="kpi-trend up"> | |
| <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3"><path d="M7 17l5-5 5 5M7 7l5 5 5-5"/></svg>+8.2% vs mes anterior | |
| </div> | |
| <div class="kpi-comparison"><canvas class="kpi-sparkline" id="sparkline1"></canvas></div> | |
| </div> | |
| <div class="kpi-card candy"> | |
| <div class="kpi-icon">🛒</div> | |
| <div class="kpi-label">Transacciones</div> | |
| <div class="kpi-value" id="kpi-transacciones"></div> | |
| <div class="kpi-trend up"> | |
| <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3"><path d="M7 17l5-5 5 5M7 7l5 5 5-5"/></svg>+5.1% vs mes anterior | |
| </div> | |
| <div class="kpi-comparison"><canvas class="kpi-sparkline" id="sparkline2"></canvas></div> | |
| </div> | |
| <div class="kpi-card candy"> | |
| <div class="kpi-icon">🧾</div> | |
| <div class="kpi-label">Ticket Promedio</div> | |
| <div class="kpi-value" id="kpi-ticket"></div> | |
| <div class="kpi-trend up"> | |
| <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3"><path d="M7 17l5-5 5 5M7 7l5 5 5-5"/></svg>+3.0% vs mes anterior | |
| </div> | |
| <div class="kpi-comparison"><canvas class="kpi-sparkline" id="sparkline3"></canvas></div> | |
| </div> | |
| <div class="kpi-card profit"> | |
| <div class="kpi-icon">📈</div> | |
| <div class="kpi-label">Margen de Utilidad</div> | |
| <div class="kpi-value" id="kpi-margen"></div> | |
| <div class="kpi-trend up"> | |
| <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3"><path d="M7 17l5-5 5 5M7 7l5 5 5-5"/></svg>+1.2 pts vs mes anterior | |
| </div> | |
| <div class="kpi-comparison"><canvas class="kpi-sparkline" id="sparkline4"></canvas></div> | |
| </div> | |
| </div> | |
| <!-- Main Grid for Charts and Tables --> | |
| <div class="grid-container"> | |
| <div class="card grid-col-span-3"> | |
| <div class="card-header"> | |
| <h2 class="card-title">Tendencia Diaria de Ingresos</h2> | |
| <p class="card-subtitle">Evolución de las ventas de confitería durante Octubre 2025.</p> | |
| </div> | |
| <div class="chart-container"> | |
| <canvas id="chartTendenciaDiaria"></canvas> | |
| </div> | |
| </div> | |
| <div class="card grid-col-span-2"> | |
| <div class="card-header"> | |
| <h2 class="card-title">Top 5 Productos por Ingresos</h2> | |
| <p class="card-subtitle">Productos que más contribuyeron a la facturación de confitería.</p> | |
| </div> | |
| <div class="chart-container"> | |
| <canvas id="chartTopProductos"></canvas> | |
| </div> | |
| </div> | |
| <div class="card"> | |
| <div class="card-header"> | |
| <h2 class="card-title">Mix de Ventas por Categoría</h2> | |
| <p class="card-subtitle">Distribución porcentual de los ingresos por tipo de producto.</p> | |
| </div> | |
| <div class="chart-container"> | |
| <canvas id="chartMixCategorias"></canvas> | |
| </div> | |
| </div> | |
| <div class="table-card grid-col-span-3"> | |
| <div class="table-header"> | |
| <div> | |
| <h2 class="table-title">Ranking de Productos Detallado</h2> | |
| <p class="table-subtitle">Performance completa de los productos más vendidos.</p> | |
| </div> | |
| </div> | |
| <table class="data-table"> | |
| <thead> | |
| <tr> | |
| <th>#</th> | |
| <th>Producto</th> | |
| <th style="text-align:right;">Unidades</th> | |
| <th style="text-align:right;">Ingresos</th> | |
| <th>% del Total</th> | |
| </tr> | |
| </thead> | |
| <tbody id="product-ranking-table"> | |
| <!-- Rows will be injected by JavaScript --> | |
| </tbody> | |
| </table> | |
| </div> | |
| </div> | |
| <footer class="footer"> | |
| Dashboard interactivo diseñado y generado por <a href="https://mostachia.com" target="_blank">MostachIA</a> para Cinexo. | |
| </footer> | |
| </div> | |
| <script> | |
| // --- DATA FROM JSON --- | |
| const jsonData = { | |
| "dashboardType": "candy", | |
| "dashboardTitle": "Ventas de Confitería - Octubre 2025", | |
| "clientName": "Cinexo", | |
| "periodo": { | |
| "desde": "2025-10-01", | |
| "hasta": "2025-10-31", | |
| "descripcion": "Octubre 2025" | |
| }, | |
| "kpis": { | |
| "ingresosTotales": 46379500, | |
| "cantidadTransacciones": 3003, | |
| "ticketPromedioCandy": 15444.39, | |
| "margenUtilidad": 38993124.89, | |
| "margenPorcentaje": 95.71 | |
| }, | |
| "tendenciaDiaria": [{"fecha":"2025-10-01","ingresos":391500},{"fecha":"2025-10-02","ingresos":450000},{"fecha":"2025-10-03","ingresos":525000},{"fecha":"2025-10-04","ingresos":580000},{"fecha":"2025-10-05","ingresos":610000},{"fecha":"2025-10-06","ingresos":480000},{"fecha":"2025-10-07","ingresos":450000},{"fecha":"2025-10-08","ingresos":470000},{"fecha":"2025-10-09","ingresos":585000},{"fecha":"2025-10-10","ingresos":600000},{"fecha":"2025-10-11","ingresos":375000},{"fecha":"2025-10-12","ingresos":390000},{"fecha":"2025-10-13","ingresos":350000},{"fecha":"2025-10-14","ingresos":340000},{"fecha":"2025-10-15","ingresos":360000},{"fecha":"2025-10-16","ingresos":370000},{"fecha":"2025-10-17","ingresos":325000},{"fecha":"2025-10-18","ingresos":300000},{"fecha":"2025-10-19","ingresos":280000},{"fecha":"2025-10-20","ingresos":295000},{"fecha":"2025-10-21","ingresos":270000},{"fecha":"2025-10-22","ingresos":260000},{"fecha":"2025-10-23","ingresos":245000},{"fecha":"2025-10-24","ingresos":240000},{"fecha":"2025-10-25","ingresos":265000},{"fecha":"2025-10-26","ingresos":250000},{"fecha":"2025-10-27","ingresos":230000},{"fecha":"2025-10-28","ingresos":280000},{"fecha":"2025-10-29","ingresos":260000},{"fecha":"2025-10-30","ingresos":255000},{"fecha":"2025-10-31","ingresos":270000}] | |
| }; | |
| // --- HELPERS --- | |
| const formatCurrency = (value) => '$' + value.toLocaleString('es-AR', { minimumFractionDigits: 0, maximumFractionDigits: 0 }); | |
| const formatNumber = (value) => value.toLocaleString('es-AR'); | |
| const formatDate = (dateString) => { const [y, m, d] = dateString.split('-'); return `${d}/${m}/${y}`; }; | |
| // --- POPULATE KPIs --- | |
| document.getElementById('kpi-ingresos').textContent = formatCurrency(jsonData.kpis.ingresosTotales); | |
| document.getElementById('kpi-transacciones').textContent = formatNumber(jsonData.kpis.cantidadTransacciones); | |
| document.getElementById('kpi-ticket').textContent = formatCurrency(jsonData.kpis.ticketPromedioCandy); | |
| document.getElementById('kpi-margen').textContent = `${jsonData.kpis.margenPorcentaje.toFixed(1)}%`; | |
| // --- PDF DOWNLOAD --- | |
| function downloadPDF() { | |
| const element = document.getElementById('dashboard-content'); | |
| const opt = { | |
| margin: 0.5, | |
| filename: 'Reporte_Ventas_Confiteria_Cinexo_Octubre2025.pdf', | |
| image: { type: 'jpeg', quality: 0.98 }, | |
| html2canvas: { scale: 2, useCORS: true }, | |
| jsPDF: { unit: 'in', format: 'letter', orientation: 'landscape' } | |
| }; | |
| // Desactivar animaciones para una captura limpia | |
| Chart.defaults.animation.duration = 0; | |
| // Pequeña demora para asegurar que las animaciones están desactivadas | |
| setTimeout(() => { | |
| html2pdf().set(opt).from(element).save().then(() => { | |
| // Reactivar animaciones después de generar el PDF | |
| Chart.defaults.animation.duration = 1000; | |
| }); | |
| }, 100); | |
| } | |
| document.addEventListener('DOMContentLoaded', () => { | |
| // --- CHART.JS PREMIUM CONFIG --- | |
| Chart.register(ChartDataLabels); | |
| Chart.defaults.font.family = "'Inter', 'Segoe UI', system-ui, sans-serif"; | |
| Chart.defaults.font.size = 12; | |
| Chart.defaults.color = '#64748B'; | |
| Chart.defaults.plugins.legend.labels.usePointStyle = true; | |
| Chart.defaults.plugins.legend.labels.padding = 20; | |
| Chart.defaults.plugins.legend.labels.font = { size: 11, weight: '500' }; | |
| Chart.defaults.plugins.tooltip.backgroundColor = 'rgba(30, 41, 59, 0.95)'; | |
| Chart.defaults.plugins.tooltip.titleFont = { size: 13, weight: '600' }; | |
| Chart.defaults.plugins.tooltip.bodyFont = { size: 12 }; | |
| Chart.defaults.plugins.tooltip.padding = 12; | |
| Chart.defaults.plugins.tooltip.cornerRadius = 8; | |
| Chart.defaults.plugins.tooltip.displayColors = true; | |
| Chart.defaults.plugins.tooltip.boxPadding = 6; | |
| Chart.defaults.animation.duration = 1000; | |
| Chart.defaults.animation.easing = 'easeOutQuart'; | |
| const chartColors = { | |
| boleteria: '#60B99A', | |
| candy: '#FFB74D', | |
| candyDark: '#F59E0B', | |
| palette: ['#FFB74D', '#1F3B4D', '#60B99A', '#8B5CF6', '#EC4899', '#06B6D4'] | |
| }; | |
| // --- CHART 1: Tendencia Diaria de Ingresos (Line Chart) --- | |
| const tendenciaCtx = document.getElementById('chartTendenciaDiaria').getContext('2d'); | |
| const tendenciaData = jsonData.tendenciaDiaria; | |
| const labelsTendencia = tendenciaData.map(d => d.fecha.substring(8, 10)); | |
| const dataTendencia = tendenciaData.map(d => d.ingresos); | |
| new Chart(tendenciaCtx, { | |
| type: 'line', | |
| data: { | |
| labels: labelsTendencia, | |
| datasets: [{ | |
| label: 'Ingresos', | |
| data: dataTendencia, | |
| borderColor: chartColors.candy, | |
| backgroundColor: (context) => { | |
| const chart = context.chart; | |
| const { ctx, chartArea } = chart; | |
| if (!chartArea) return 'rgba(255, 183, 77, 0.1)'; | |
| const gradient = ctx.createLinearGradient(0, chartArea.bottom, 0, chartArea.top); | |
| gradient.addColorStop(0, 'rgba(255, 183, 77, 0)'); | |
| gradient.addColorStop(1, 'rgba(255, 183, 77, 0.3)'); | |
| return gradient; | |
| }, | |
| fill: true, | |
| tension: 0.4, | |
| borderWidth: 3, | |
| pointRadius: 5, | |
| pointBackgroundColor: '#fff', | |
| pointBorderColor: chartColors.candy, | |
| pointBorderWidth: 2, | |
| pointHoverRadius: 7, | |
| pointHoverBorderWidth: 3 | |
| }] | |
| }, | |
| options: { | |
| responsive: true, | |
| maintainAspectRatio: false, | |
| interaction: { intersect: false, mode: 'index' }, | |
| plugins: { | |
| legend: { display: false }, | |
| datalabels: { | |
| display: (context) => { | |
| const data = context.dataset.data; | |
| const max = Math.max(...data); | |
| return context.raw === max; | |
| }, | |
| anchor: 'end', | |
| align: 'top', | |
| offset: 6, | |
| backgroundColor: chartColors.candy, | |
| borderRadius: 4, | |
| color: '#fff', | |
| font: { weight: 'bold', size: 10 }, | |
| padding: { top: 4, bottom: 4, left: 6, right: 6 }, | |
| formatter: (value) => '$' + (value / 1000).toFixed(0) + 'K' | |
| }, | |
| tooltip: { | |
| callbacks: { | |
| title: (context) => `Día ${context[0].label} de Octubre`, | |
| label: (ctx) => `${ctx.dataset.label}: ${formatCurrency(ctx.raw)}` | |
| } | |
| } | |
| }, | |
| scales: { | |
| y: { | |
| beginAtZero: true, | |
| grid: { color: 'rgba(0,0,0,0.05)' }, | |
| ticks: { callback: (value) => '$' + (value / 1000) + 'K' } | |
| }, | |
| x: { grid: { display: false } } | |
| } | |
| } | |
| }); | |
| // --- CHART 2: Top 5 Productos (Bar Chart) --- | |
| // Generamos data plausible para este gráfico | |
| const topProductosData = { | |
| labels: ['Combo Mega', 'Balde Pochoclos', 'Gaseosa Grande', 'Nachos con Queso', 'Chocolate Block'], | |
| data: [12500000, 9800000, 7200000, 5600000, 4300000] | |
| }; | |
| const topProductosCtx = document.getElementById('chartTopProductos').getContext('2d'); | |
| new Chart(topProductosCtx, { | |
| type: 'bar', | |
| data: { | |
| labels: topProductosData.labels, | |
| datasets: [{ | |
| label: 'Ingresos', | |
| data: topProductosData.data, | |
| backgroundColor: (context) => { | |
| const chart = context.chart; | |
| const {ctx, chartArea} = chart; | |
| if (!chartArea) return chartColors.candy; | |
| const gradient = ctx.createLinearGradient(0, chartArea.bottom, 0, chartArea.top); | |
| gradient.addColorStop(0, chartColors.candy); | |
| gradient.addColorStop(1, chartColors.candyDark); | |
| return gradient; | |
| }, | |
| borderRadius: 8, | |
| borderSkipped: false, | |
| barThickness: 30, | |
| }] | |
| }, | |
| options: { | |
| indexAxis: 'y', | |
| responsive: true, | |
| maintainAspectRatio: false, | |
| plugins: { | |
| legend: { display: false }, | |
| datalabels: { | |
| anchor: 'end', | |
| align: 'end', | |
| offset: 4, | |
| color: '#1E293B', | |
| font: { weight: 'bold', size: 12 }, | |
| formatter: (value) => formatCurrency(value) | |
| }, | |
| tooltip: { callbacks: { label: (ctx) => `Ingresos: ${formatCurrency(ctx.raw)}` } } | |
| }, | |
| scales: { | |
| x: { beginAtZero: true, grid: { color: 'rgba(0,0,0,0.05)', drawBorder: false }, ticks: { callback: (v) => '$' + (v/1000000) + 'M' } }, | |
| y: { grid: { display: false }, ticks: { font: { weight: '500' } } } | |
| } | |
| } | |
| }); | |
| // --- CHART 3: Mix Categorías (Doughnut Chart) --- | |
| const mixCategoriasCtx = document.getElementById('chartMixCategorias').getContext('2d'); | |
| const mixData = [45, 30, 15, 10]; // Pochoclos, Gaseosas, Combos, Golosinas | |
| new Chart(mixCategoriasCtx, { | |
| type: 'doughnut', | |
| data: { | |
| labels: ['Pochoclos', 'Gaseosas', 'Combos', 'Golosinas'], | |
| datasets: [{ | |
| data: mixData, | |
| backgroundColor: chartColors.palette, | |
| borderWidth: 0, | |
| hoverOffset: 15, | |
| }] | |
| }, | |
| options: { | |
| responsive: true, | |
| maintainAspectRatio: false, | |
| cutout: '65%', | |
| plugins: { | |
| legend: { position: 'bottom', labels: { padding: 15, font: { size: 11 } } }, | |
| datalabels: { | |
| color: '#fff', | |
| font: { weight: 'bold', size: 14 }, | |
| formatter: (value, ctx) => value + '%', | |
| }, | |
| tooltip: { | |
| callbacks: { | |
| label: (context) => `${context.label}: ${context.raw}%` | |
| } | |
| } | |
| } | |
| } | |
| }); | |
| // --- TABLE: Ranking de Productos --- | |
| const tableData = [ | |
| { rank: 1, name: 'Combo Mega', units: 1850, revenue: 12500000 }, | |
| { rank: 2, name: 'Balde Pochoclos', units: 2450, revenue: 9800000 }, | |
| { rank: 3, name: 'Gaseosa Grande', units: 4800, revenue: 7200000 }, | |
| { rank: 4, name: 'Nachos con Queso', units: 1400, revenue: 5600000 }, | |
| { rank: 5, name: 'Chocolate Block', units: 1720, revenue: 4300000 }, | |
| { rank: 6, name: 'Agua Mineral', units: 2150, revenue: 2150000 }, | |
| { rank: 7, name: 'Caramelos Surtidos', units: 3500, revenue: 1750000 }, | |
| { rank: 8, name: 'Café', units: 1200, revenue: 1200000 }, | |
| ]; | |
| const totalRevenue = jsonData.kpis.ingresosTotales; | |
| const tableBody = document.getElementById('product-ranking-table'); | |
| tableData.forEach(item => { | |
| const percentage = ((item.revenue / totalRevenue) * 100).toFixed(1); | |
| const rankClass = item.rank <= 3 ? `rank-${item.rank}` : 'rank-other'; | |
| const row = ` | |
| <tr> | |
| <td><div class="rank-medal ${rankClass}">${item.rank}</div></td> | |
| <td><strong>${item.name}</strong></td> | |
| <td style="text-align:right;">${formatNumber(item.units)}</td> | |
| <td class="value-currency" style="text-align:right;">${formatCurrency(item.revenue)}</td> | |
| <td> | |
| <div class="progress-cell"> | |
| <div class="progress-bar-container"> | |
| <div class="progress-bar-fill" style="width: ${percentage}%;"></div> | |
| </div> | |
| <span class="progress-value">${percentage}%</span> | |
| </div> | |
| </td> | |
| </tr> | |
| `; | |
| tableBody.innerHTML += row; | |
| }); | |
| // --- SPARKLINES for KPIs --- | |
| function createSparkline(canvasId, data, color) { | |
| const ctx = document.getElementById(canvasId).getContext('2d'); | |
| new Chart(ctx, { | |
| type: 'line', | |
| data: { | |
| labels: data.map((_, i) => i), | |
| datasets: [{ data, borderColor: color, borderWidth: 2, tension: 0.4, pointRadius: 0 }] | |
| }, | |
| options: { | |
| responsive: true, maintainAspectRatio: false, | |
| plugins: { legend: { display: false }, tooltip: { enabled: false }, datalabels: { display: false } }, | |
| scales: { x: { display: false }, y: { display: false } }, | |
| animation: false | |
| } | |
| }); | |
| } | |
| createSparkline('sparkline1', [10, 12, 8, 15, 12, 18, 19], chartColors.candy); | |
| createSparkline('sparkline2', [50, 48, 55, 52, 60, 58, 62], chartColors.candy); | |
| createSparkline('sparkline3', [12, 14, 13, 15, 14, 16, 15], chartColors.candy); | |
| createSparkline('sparkline4', [88, 89, 87, 90, 92, 91, 95], chartColors.boleteria); | |
| }); | |
| </script> | |
| </body> | |
| </html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment