Created
December 29, 2025 03:13
-
-
Save Jcbertorello/2e769356269f3bcbc28d61ca20797357 to your computer and use it in GitHub Desktop.
Dashboard Ajonjolí - 2025-12-29 03:13
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>Resumen Ejecutivo de Rendimiento - 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 para exportar --> | |
| <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: 1320px; | |
| 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(96, 185, 154, 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-primary) 0%, var(--color-primary-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(96, 185, 154, 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-primary), var(--color-primary-dark)); | |
| color: white; | |
| box-shadow: 0 4px 12px rgba(96, 185, 154, 0.4); | |
| } | |
| .btn-primary:hover { transform: translateY(-2px); box-shadow: 0 6px 20px rgba(96, 185, 154, 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; | |
| flex-wrap: wrap; | |
| gap: 16px; | |
| box-shadow: var(--shadow-md); | |
| border-left: 4px solid var(--color-primary); | |
| } | |
| .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; } | |
| /* === LAYOUT & CARDS === */ | |
| .grid { display: grid; gap: 24px; } | |
| .grid-2 { grid-template-columns: repeat(2, 1fr); } | |
| .grid-3 { grid-template-columns: repeat(3, 1fr); } | |
| .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; | |
| transition: all 0.3s ease; | |
| } | |
| .card:hover { transform: translateY(-3px); box-shadow: var(--shadow-lg); } | |
| .card-header { margin-bottom: 20px; } | |
| .card-title { font-size: 1.1rem; font-weight: 700; color: var(--text-primary); } | |
| .card-subtitle { font-size: 0.8rem; color: var(--text-muted); margin-top: 2px; } | |
| .chart-container { position: relative; flex-grow: 1; min-height: 300px; } | |
| /* === KPI CARDS PREMIUM === */ | |
| .kpi-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 24px; margin-bottom: 24px; } | |
| .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-primary), var(--color-primary-light)); | |
| } | |
| .kpi-card.candy::before { background: linear-gradient(90deg, var(--color-candy), var(--color-candy-dark)); } | |
| .kpi-card.total::before { background: linear-gradient(90deg, var(--color-secondary), var(--color-secondary-light)); } | |
| .kpi-card:hover { transform: translateY(-4px); box-shadow: var(--shadow-xl), var(--shadow-glow); border-color: var(--color-primary); } | |
| .kpi-icon { | |
| width: 48px; height: 48px; border-radius: 12px; display: flex; align-items: center; | |
| justify-content: center; font-size: 1.5rem; margin-bottom: 16px; | |
| background: linear-gradient(135deg, rgba(96, 185, 154, 0.1), rgba(96, 185, 154, 0.2)); | |
| } | |
| .kpi-card.candy .kpi-icon { background: linear-gradient(135deg, rgba(255, 183, 77, 0.1), rgba(255, 183, 77, 0.2)); } | |
| .kpi-card.total .kpi-icon { background: linear-gradient(135deg, rgba(31, 59, 77, 0.1), rgba(31, 59, 77, 0.2)); } | |
| .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-detail { font-size: 0.85rem; color: var(--text-secondary); margin-bottom: 8px; } | |
| .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%; } | |
| /* === TABLAS PREMIUM === */ | |
| .table-card { padding: 0; } | |
| .table-header { padding: 20px 24px; border-bottom: 1px solid var(--border-light); display: flex; justify-content: space-between; align-items: center; } | |
| .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: linear-gradient(180deg, var(--bg-hover), var(--bg-primary)); } | |
| .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(96, 185, 154, 0.05); } | |
| .data-table tbody tr:last-child td { border-bottom: none; } | |
| .rank-medal { | |
| width: 32px; height: 32px; border-radius: 50%; display: inline-flex; | |
| align-items: center; justify-content: center; font-weight: 800; font-size: 0.85rem; | |
| } | |
| .rank-1 { background: linear-gradient(135deg, #FFD700, #FFA500); color: white; box-shadow: 0 2px 8px rgba(255, 215, 0, 0.4); } | |
| .rank-2 { background: linear-gradient(135deg, #E8E8E8, #B8B8B8); color: white; box-shadow: 0 2px 8px rgba(192, 192, 192, 0.4); } | |
| .rank-3 { background: linear-gradient(135deg, #CD7F32, #8B4513); color: white; box-shadow: 0 2px 8px rgba(205, 127, 50, 0.4); } | |
| .rank-other { background: var(--bg-hover); color: var(--text-muted); } | |
| .value-highlight { font-weight: 700; color: var(--text-primary); } | |
| .value-currency { font-feature-settings: 'tnum'; } | |
| /* === FOOTER PREMIUM === */ | |
| .footer { margin-top: 40px; padding: 24px; text-align: center; } | |
| .mostachia-brand { display: inline-flex; align-items: center; gap: 12px; } | |
| .mostachia-logo { | |
| width: 40px; height: 40px; background: linear-gradient(135deg, var(--color-primary), var(--color-primary-dark)); | |
| border-radius: 10px; display: flex; align-items: center; justify-content: center; | |
| color: white; font-weight: 800; font-size: 1.1rem; | |
| } | |
| .mostachia-text { font-size: 0.85rem; color: var(--text-muted); text-align: left; } | |
| .mostachia-text strong { display: block; color: var(--text-primary); font-weight: 700; } | |
| /* === PRINT/PDF === */ | |
| @media print { | |
| body { background: white !important; -webkit-print-color-adjust: exact; print-color-adjust: exact; } | |
| .btn, .header-actions, .footer { display: none !important; } | |
| .dashboard { padding: 16px !important; max-width: 100% !important; margin: 0; } | |
| .header { box-shadow: none !important; border: 1px solid #ddd !important; } | |
| .card, .kpi-card { page-break-inside: avoid; box-shadow: none !important; border: 1px solid #ddd !important; } | |
| .grid { grid-template-columns: 1fr !important; } | |
| .kpi-grid { grid-template-columns: 1fr 1fr !important; } | |
| } | |
| /* === RESPONSIVE === */ | |
| @media (max-width: 1024px) { | |
| .grid-2, .grid-3 { grid-template-columns: 1fr; } | |
| } | |
| @media (max-width: 768px) { | |
| .header { flex-direction: column; gap: 16px; text-align: center; } | |
| .info-bar { justify-content: center; } | |
| .kpi-grid { grid-template-columns: 1fr 1fr; } | |
| } | |
| @media (max-width: 500px) { | |
| .kpi-grid { grid-template-columns: 1fr; } | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="dashboard" id="dashboard-to-print"> | |
| <header class="header"> | |
| <div class="header-left"> | |
| <div class="logo">CX</div> | |
| <div> | |
| <h1 class="header-title" id="dashboard-title"></h1> | |
| <p class="header-subtitle" id="dashboard-subtitle"></p> | |
| </div> | |
| </div> | |
| <div class="header-actions"> | |
| <button class="btn btn-primary" onclick="downloadPDF()"> | |
| <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path><polyline points="7 10 12 15 17 10"></polyline><line x1="12" y1="15" x2="12" y2="3"></line></svg> | |
| Descargar PDF | |
| </button> | |
| </div> | |
| </header> | |
| <div class="info-bar"> | |
| <div class="info-item"> | |
| <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="4" width="18" height="18" rx="2" ry="2"></rect><line x1="16" y1="2" x2="16" y2="6"></line><line x1="8" y1="2" x2="8" y2="6"></line><line x1="3" y1="10" x2="21" y2="10"></line></svg> | |
| <span>Período: <strong id="info-periodo"></strong></span> | |
| </div> | |
| <div class="info-item"> | |
| <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14.5 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7.5L14.5 2z"></path><polyline points="14 2 14 8 20 8"></polyline><line x1="16" y1="13" x2="8" y2="13"></line><line x1="16" y1="17" x2="8" y2="17"></line><line x1="10" y1="9" x2="8" y2="9"></line></svg> | |
| <span>Funciones Realizadas: <strong id="info-funciones"></strong></span> | |
| </div> | |
| <div class="info-item"> | |
| <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 22c5.523 0 10-4.477 10-10S17.523 2 12 2 2 6.477 2 12s4.477 10 10 10z"></path><path d="m9 12 2 2 4-4"></path></svg> | |
| <span>Ocupación Promedio: <strong id="info-ocupacion"></strong></span> | |
| </div> | |
| </div> | |
| <section class="kpi-grid"> | |
| <div class="kpi-card total"> | |
| <div class="kpi-icon">💰</div> | |
| <div class="kpi-label">Ingresos Totales</div> | |
| <div class="kpi-value" id="kpi-ingresos"></div> | |
| <div class="kpi-detail" id="kpi-cpp"></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="M18 15l-6-6-6 6"/></svg> | |
| +8.2% vs mes ant. | |
| </div> | |
| </div> | |
| <div class="kpi-card"> | |
| <div class="kpi-icon">🎟️</div> | |
| <div class="kpi-label">Boletería</div> | |
| <div class="kpi-value" id="kpi-boleteria"></div> | |
| <div class="kpi-detail" id="kpi-ticket-boleteria"></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">Candy</div> | |
| <div class="kpi-value" id="kpi-candy"></div> | |
| <div class="kpi-detail" id="kpi-ticket-candy"></div> | |
| <div class="kpi-comparison"> | |
| <canvas class="kpi-sparkline" id="sparkline2"></canvas> | |
| </div> | |
| </div> | |
| <div class="kpi-card"> | |
| <div class="kpi-icon">👥</div> | |
| <div class="kpi-label">Espectadores</div> | |
| <div class="kpi-value" id="kpi-espectadores"></div> | |
| <div class="kpi-detail" id="kpi-ventas-online"></div> | |
| <div class="kpi-comparison"> | |
| <canvas class="kpi-sparkline" id="sparkline3"></canvas> | |
| </div> | |
| </div> | |
| </section> | |
| <main class="grid grid-2"> | |
| <div class="card"> | |
| <div class="card-header"> | |
| <h2 class="card-title">Top 5 Películas por Espectadores</h2> | |
| <p class="card-subtitle">Ranking de las películas más vistas en el período</p> | |
| </div> | |
| <div class="chart-container"> | |
| <canvas id="chartPeliculas"></canvas> | |
| </div> | |
| </div> | |
| <div class="card"> | |
| <div class="card-header"> | |
| <h2 class="card-title">Mix de Ingresos</h2> | |
| <p class="card-subtitle">Distribución porcentual de los ingresos totales</p> | |
| </div> | |
| <div class="chart-container"> | |
| <canvas id="chartIngresosFuente"></canvas> | |
| </div> | |
| </div> | |
| <div class="card table-card"> | |
| <div class="table-header"> | |
| <div> | |
| <h2 class="table-title">Top 5 Productos del Candy</h2> | |
| <p class="table-subtitle">Productos más vendidos por recaudación</p> | |
| </div> | |
| </div> | |
| <table class="data-table" id="tablaTopCandy"> | |
| <thead> | |
| <tr> | |
| <th style="width: 50px; text-align: center;">#</th> | |
| <th>Producto</th> | |
| <th style="text-align: right;">Cantidad</th> | |
| <th style="text-align: right;">Recaudación</th> | |
| </tr> | |
| </thead> | |
| <tbody> | |
| <!-- Filas insertadas por JS --> | |
| </tbody> | |
| </table> | |
| </div> | |
| <div class="card"> | |
| <div class="card-header"> | |
| <h2 class="card-title">Ocupación por Sala</h2> | |
| <p class="card-subtitle">Porcentaje de ocupación promedio para cada sala</p> | |
| </div> | |
| <div class="chart-container"> | |
| <canvas id="chartOcupacionSalas"></canvas> | |
| </div> | |
| </div> | |
| </main> | |
| <footer class="footer"> | |
| <div class="mostachia-brand"> | |
| <div class="mostachia-logo">M</div> | |
| <div class="mostachia-text"> | |
| <strong>Reporte Generado por MostachIA</strong> | |
| Análisis de Datos para la Industria del Entretenimiento | |
| </div> | |
| </div> | |
| </footer> | |
| </div> | |
| <script> | |
| // Data from JSON | |
| const data = { | |
| "dashboardType": "ejecutivo", | |
| "dashboardTitle": "Resumen Ejecutivo de Rendimiento - Cinexo", | |
| "clientName": "Cinexo", | |
| "periodo": { | |
| "desde": "2025-01-01", | |
| "hasta": "2025-01-31", | |
| "descripcion": "Enero 2025" | |
| }, | |
| "kpis": { | |
| "espectadoresTotales": 22223, | |
| "recaudacionBoleteria": 114150500, | |
| "ventaCandy": 126199500, | |
| "ingresosTotales": 240350000, | |
| "ticketPromedioBoleteria": 5136.59, | |
| "ticketPromedioCandy": 15618.75, | |
| "cpp": 11344.63, | |
| "funcionesRealizadas": 872, | |
| "ocupacionPromedio": 14.83, | |
| "porcentajeVentasOnline": 16.65 | |
| }, | |
| "topPeliculas": [ | |
| { "pelicula": "Homo argentum", "espectadores": 8180 }, | |
| { "pelicula": "F1 2D+4D", "espectadores": 2490 }, | |
| { "pelicula": "Otro viernes de locos", "espectadores": 1863 }, | |
| { "pelicula": "Los 4 Fantásticos...", "espectadores": 1707 }, | |
| { "pelicula": "La hora de la desaparición...", "espectadores": 1395 } | |
| ], | |
| "ocupacionPorSala": [ | |
| { "nombreSala": "Sala 1", "capacidad": 194, "ocupacion": 17.89 }, | |
| { "nombreSala": "Sala 4", "capacidad": 160, "ocupacion": 17.66 }, | |
| { "nombreSala": "Sala 6", "capacidad": 160, "ocupacion": 16.89 }, | |
| { "nombreSala": "Sala 5", "capacidad": 160, "ocupacion": 16.41 }, | |
| { "nombreSala": "Sala 8", "capacidad": 327, "ocupacion": 13.06 }, | |
| { "nombreSala": "Sala 3", "capacidad": 160, "ocupacion": 11.45 }, | |
| { "nombreSala": "Sala 7", "capacidad": 148, "ocupacion": 10.45 }, | |
| { "nombreSala": "Sala 2", "capacidad": 133, "ocupacion": 9.11 } | |
| ], | |
| "topProductosCandy": [ | |
| { "producto": "COMBO PAREJA", "cantidad": 2656, "total": 35780000 }, | |
| { "producto": "COMBO MEDIANO", "cantidad": 1673, "total": 15893500 }, | |
| { "producto": "AGUA", "cantidad": 1474, "total": 5795000 }, | |
| { "producto": "BALDE POP", "cantidad": 1416, "total": 17235000 }, | |
| { "producto": "COMBO BALDE", "cantidad": 1156, "total": 14988000 } | |
| ] | |
| }; | |
| // Helpers | |
| const formatCurrency = (value) => '$' + Math.round(value).toLocaleString('es-AR'); | |
| const formatNumber = (value) => Math.round(value).toLocaleString('es-AR'); | |
| // Función PDF | |
| function downloadPDF() { | |
| const element = document.getElementById('dashboard-to-print'); | |
| const opt = { | |
| margin: [0.5, 0.2, 0.5, 0.2], // [top, left, bottom, right] in inches | |
| filename: `Reporte_Cinexo_${data.periodo.descripcion.replace(' ','_')}.pdf`, | |
| image: { type: 'jpeg', quality: 0.98 }, | |
| html2canvas: { scale: 2, useCORS: true, logging: false }, | |
| jsPDF: { unit: 'in', format: 'letter', orientation: 'landscape' } | |
| }; | |
| html2pdf().set(opt).from(element).save(); | |
| } | |
| document.addEventListener('DOMContentLoaded', () => { | |
| // Populate Header & Info Bar | |
| document.getElementById('dashboard-title').textContent = data.dashboardTitle; | |
| document.getElementById('dashboard-subtitle').textContent = `Período: ${data.periodo.descripcion}`; | |
| document.getElementById('info-periodo').textContent = data.periodo.descripcion; | |
| document.getElementById('info-funciones').textContent = formatNumber(data.kpis.funcionesRealizadas); | |
| document.getElementById('info-ocupacion').textContent = `${data.kpis.ocupacionPromedio.toFixed(1)}%`; | |
| // Populate KPIs | |
| document.getElementById('kpi-ingresos').textContent = formatCurrency(data.kpis.ingresosTotales); | |
| document.getElementById('kpi-cpp').textContent = `CPP: ${formatCurrency(data.kpis.cpp)}`; | |
| document.getElementById('kpi-boleteria').textContent = formatCurrency(data.kpis.recaudacionBoleteria); | |
| document.getElementById('kpi-ticket-boleteria').textContent = `Ticket Prom: ${formatCurrency(data.kpis.ticketPromedioBoleteria)}`; | |
| document.getElementById('kpi-candy').textContent = formatCurrency(data.kpis.ventaCandy); | |
| document.getElementById('kpi-ticket-candy').textContent = `Ticket Prom: ${formatCurrency(data.kpis.ticketPromedioCandy)}`; | |
| document.getElementById('kpi-espectadores').textContent = formatNumber(data.kpis.espectadoresTotales); | |
| document.getElementById('kpi-ventas-online').textContent = `${data.kpis.porcentajeVentasOnline.toFixed(1)}% ventas online`; | |
| // Populate Top Candy Table | |
| const candyTableBody = document.querySelector('#tablaTopCandy tbody'); | |
| const rankClasses = ['rank-1', 'rank-2', 'rank-3', 'rank-other', 'rank-other']; | |
| data.topProductosCandy.forEach((item, index) => { | |
| const row = ` | |
| <tr> | |
| <td style="text-align: center;"><span class="rank-medal ${rankClasses[index]}">${index + 1}</span></td> | |
| <td>${item.producto}</td> | |
| <td class="value-highlight" style="text-align: right;">${formatNumber(item.cantidad)}</td> | |
| <td class="value-currency value-highlight" style="text-align: right;">${formatCurrency(item.total)}</td> | |
| </tr> | |
| `; | |
| candyTableBody.innerHTML += row; | |
| }); | |
| // Charting | |
| initializeCharts(); | |
| }); | |
| function initializeCharts() { | |
| // Registrar plugin | |
| Chart.register(ChartDataLabels); | |
| // Configuración global de Chart.js | |
| Chart.defaults.font.family = "'Inter', 'Segoe UI', system-ui, sans-serif"; | |
| Chart.defaults.font.size = 12; | |
| Chart.defaults.color = '#64748B'; | |
| Chart.defaults.plugins.legend.position = 'bottom'; | |
| 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 = { | |
| primary: '#60B99A', | |
| primaryLight: '#7FCDB2', | |
| boleteria: '#60B99A', | |
| candy: '#FFB74D', | |
| palette: ['#60B99A', '#1F3B4D', '#FFB74D', '#7FCDB2', '#2D5066', '#F59E0B'], | |
| ocupacionAlta: '#10B981', | |
| ocupacionMedia: '#F59E0B', | |
| ocupacionBaja: '#EF4444' | |
| }; | |
| // === GRÁFICO 1: TOP PELÍCULAS (BARRAS) === | |
| new Chart(document.getElementById('chartPeliculas'), { | |
| type: 'bar', | |
| data: { | |
| labels: data.topPeliculas.map(p => p.pelicula), | |
| datasets: [{ | |
| label: 'Espectadores', | |
| data: data.topPeliculas.map(p => p.espectadores), | |
| backgroundColor: (context) => { | |
| const chart = context.chart; | |
| const { ctx, chartArea } = chart; | |
| if (!chartArea) return chartColors.primary; | |
| const gradient = ctx.createLinearGradient(0, chartArea.bottom, 0, chartArea.top); | |
| gradient.addColorStop(0, chartColors.primaryLight); | |
| gradient.addColorStop(1, chartColors.primary); | |
| return gradient; | |
| }, | |
| borderRadius: 8, | |
| borderSkipped: false, | |
| barThickness: 35, | |
| maxBarThickness: 45 | |
| }] | |
| }, | |
| 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) => formatNumber(value) | |
| }, | |
| tooltip: { callbacks: { label: (ctx) => `${formatNumber(ctx.raw)} espectadores` } } | |
| }, | |
| scales: { | |
| x: { | |
| beginAtZero: true, | |
| grid: { color: 'rgba(0,0,0,0.05)', drawBorder: false }, | |
| ticks: { callback: (value) => value >= 1000 ? (value / 1000) + 'K' : value } | |
| }, | |
| y: { grid: { display: false }, ticks: { font: { weight: '500' } } } | |
| } | |
| } | |
| }); | |
| // === GRÁFICO 2: INGRESOS FUENTE (DONA) === | |
| new Chart(document.getElementById('chartIngresosFuente'), { | |
| type: 'doughnut', | |
| data: { | |
| labels: ['Boletería', 'Candy'], | |
| datasets: [{ | |
| data: [data.kpis.recaudacionBoleteria, data.kpis.ventaCandy], | |
| backgroundColor: [chartColors.boleteria, chartColors.candy], | |
| borderWidth: 0, hoverOffset: 15, hoverBorderWidth: 4, hoverBorderColor: '#fff' | |
| }] | |
| }, | |
| options: { | |
| responsive: true, | |
| maintainAspectRatio: false, | |
| cutout: '60%', | |
| plugins: { | |
| legend: { | |
| position: 'right', | |
| labels: { | |
| generateLabels: function(chart) { | |
| const d = chart.data; | |
| const total = d.datasets[0].data.reduce((a, b) => a + b, 0); | |
| return d.labels.map((label, i) => ({ | |
| text: `${label}: ${formatCurrency(d.datasets[0].data[i])}`, | |
| fillStyle: d.datasets[0].backgroundColor[i], | |
| hidden: false, index: i | |
| })); | |
| }, | |
| padding: 15, font: { size: 12 } | |
| } | |
| }, | |
| datalabels: { | |
| color: '#fff', font: { weight: 'bold', size: 16 }, | |
| formatter: (value, ctx) => { | |
| const total = ctx.chart.data.datasets[0].data.reduce((a, b) => a + b, 0); | |
| const percentage = ((value / total) * 100).toFixed(1); | |
| return percentage + '%'; | |
| }, | |
| anchor: 'center', align: 'center', offset: 0 | |
| }, | |
| tooltip: { | |
| callbacks: { | |
| label: function(context) { | |
| const total = context.dataset.data.reduce((a, b) => a + b, 0); | |
| const value = context.raw; | |
| const percentage = ((value / total) * 100).toFixed(1); | |
| return `${context.label}: ${formatCurrency(value)} (${percentage}%)`; | |
| } | |
| } | |
| } | |
| } | |
| } | |
| }); | |
| // === GRÁFICO 3: OCUPACIÓN SALAS (BARRAS) === | |
| new Chart(document.getElementById('chartOcupacionSalas'), { | |
| type: 'bar', | |
| data: { | |
| labels: data.ocupacionPorSala.map(s => s.nombreSala), | |
| datasets: [{ | |
| label: 'Ocupación', | |
| data: data.ocupacionPorSala.map(s => s.ocupacion), | |
| backgroundColor: data.ocupacionPorSala.map(s => { | |
| if (s.ocupacion >= 17) return chartColors.ocupacionAlta; | |
| if (s.ocupacion >= 12) return chartColors.ocupacionMedia; | |
| return chartColors.ocupacionBaja; | |
| }), | |
| borderRadius: 6, | |
| borderSkipped: false, | |
| barThickness: 20 | |
| }] | |
| }, | |
| options: { | |
| indexAxis: 'y', | |
| responsive: true, | |
| maintainAspectRatio: false, | |
| plugins: { | |
| legend: { display: false }, | |
| datalabels: { | |
| anchor: 'end', | |
| align: 'end', | |
| offset: 4, | |
| color: '#1E293B', | |
| font: { weight: 'bold', size: 11 }, | |
| formatter: (value) => `${value.toFixed(1)}%` | |
| }, | |
| tooltip: { callbacks: { label: (ctx) => `Ocupación: ${ctx.raw.toFixed(1)}%` } } | |
| }, | |
| scales: { | |
| x: { beginAtZero: true, grid: { color: 'rgba(0,0,0,0.05)' }, | |
| ticks: { callback: (value) => value + '%' } | |
| }, | |
| y: { grid: { display: false } } | |
| } | |
| } | |
| }); | |
| // === SPARKLINES para KPIs === | |
| function createSparkline(canvasId, dataPoints, color) { | |
| const ctx = document.getElementById(canvasId).getContext('2d'); | |
| new Chart(ctx, { | |
| type: 'line', | |
| data: { | |
| labels: dataPoints.map((_, i) => i), | |
| datasets: [{ | |
| data: dataPoints, borderColor: color, borderWidth: 2, | |
| fill: false, 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, 15], chartColors.boleteria); | |
| createSparkline('sparkline2', [50, 48, 55, 52, 60, 58, 65], chartColors.candy); | |
| createSparkline('sparkline3', [20, 22, 19, 25, 23, 28, 26], chartColors.primary); | |
| } | |
| </script> | |
| </body> | |
| </html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment