Created
December 29, 2025 00:16
-
-
Save Jcbertorello/336f5747b982c02786f9dd6c58283ef3 to your computer and use it in GitHub Desktop.
Dashboard Ajonjolí - 2025-12-29 00:16
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>Ocupación y Facturación por Sala - Septiembre 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 & BASE === */ | |
| * { margin: 0; padding: 0; box-sizing: border-box; } | |
| html { scroll-behavior: smooth; } | |
| 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: 32px; | |
| 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); | |
| } | |
| /* === 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-primary), var(--color-primary-light)); | |
| } | |
| .kpi-card.secondary::before { background: linear-gradient(90deg, var(--color-secondary), var(--color-secondary-light)); } | |
| .kpi-card.warning::before { background: linear-gradient(90deg, var(--color-candy), var(--color-candy-dark)); } | |
| .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.secondary .kpi-icon { background: linear-gradient(135deg, rgba(31, 59, 77, 0.1), rgba(31, 59, 77, 0.2)); } | |
| .kpi-card.warning .kpi-icon { background: linear-gradient(135deg, rgba(255, 183, 77, 0.1), rgba(255, 183, 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-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); } | |
| /* === GRID LAYOUT === */ | |
| .grid-layout { display: grid; grid-template-columns: repeat(3, 1fr); gap: 24px; } | |
| .col-span-2 { grid-column: span 2; } | |
| .col-span-1 { grid-column: span 1; } | |
| .col-span-3 { grid-column: span 3; } | |
| /* === CHART CARD === */ | |
| .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 { padding-bottom: 16px; border-bottom: 1px solid var(--border-light); margin-bottom: 16px; } | |
| .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; } | |
| .h-400 { height: 400px; } | |
| .h-832 { height: 832px; } | |
| /* === TABLE PREMIUM === */ | |
| .table-card { | |
| background: var(--bg-card); | |
| border-radius: 16px; | |
| overflow: hidden; | |
| box-shadow: var(--shadow-md); | |
| border: 1px solid var(--border-light); | |
| } | |
| .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(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); } | |
| .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; } | |
| .progress-bar-fill.high { background: linear-gradient(90deg, #10B981, #34D399); } | |
| .progress-bar-fill.medium { background: linear-gradient(90deg, #F59E0B, #FBBF24); } | |
| .progress-bar-fill.low { background: linear-gradient(90deg, #EF4444, #F87171); } | |
| .progress-value { font-weight: 700; font-size: 0.85rem; min-width: 45px; text-align: right; } | |
| .value-highlight { font-weight: 700; color: var(--text-primary); } | |
| .value-currency { font-feature-settings: 'tnum'; } | |
| /* === FOOTER PREMIUM === */ | |
| .footer { text-align: center; padding-top: 32px; color: var(--text-muted); font-size: 0.8rem; } | |
| .footer a { color: var(--color-primary-dark); text-decoration: none; font-weight: 600; } | |
| .footer a:hover { text-decoration: underline; } | |
| /* === PRINT/PDF === */ | |
| @media print { | |
| body { background: white !important; -webkit-print-color-adjust: exact; print-color-adjust: exact; } | |
| .btn, .header-actions { display: none !important; } | |
| .dashboard { padding: 16px !important; max-width: 100% !important; margin: 0 !important; } | |
| .header, .card, .kpi-card, .table-card { box-shadow: none !important; border: 1px solid #ddd !important; break-inside: avoid; } | |
| .grid-layout { display: block; } /* Stacks elements for printing */ | |
| .card, .kpi-card { margin-bottom: 20px; } | |
| } | |
| /* === RESPONSIVE === */ | |
| @media (max-width: 1200px) { | |
| .grid-layout { grid-template-columns: 1fr 1fr; } | |
| .col-span-3 { grid-column: span 2; } | |
| .h-832 { height: auto; } | |
| } | |
| @media (max-width: 768px) { | |
| .header { flex-direction: column; gap: 16px; text-align: center; } | |
| .grid-layout { grid-template-columns: 1fr; } | |
| .col-span-1, .col-span-2, .col-span-3 { grid-column: span 1; } | |
| .h-400 { height: 300px; } | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="dashboard" id="dashboard-content"> | |
| <!-- Header --> | |
| <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="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> | |
| <!-- KPIs --> | |
| <div class="kpi-grid"> | |
| <div class="kpi-card"> | |
| <div class="kpi-icon">💰</div> | |
| <div class="kpi-label">Recaudación Total</div> | |
| <div class="kpi-value" id="kpi-recaudacion"></div> | |
| <div class="kpi-trend up"> | |
| <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><path d="M12 19V5M5 12l7-7 7 7"/></svg> | |
| +8.2% vs mes anterior | |
| </div> | |
| </div> | |
| <div class="kpi-card secondary"> | |
| <div class="kpi-icon">🎟️</div> | |
| <div class="kpi-label">Total Espectadores</div> | |
| <div class="kpi-value" id="kpi-espectadores"></div> | |
| <div class="kpi-trend up"> | |
| <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><path d="M12 19V5M5 12l7-7 7 7"/></svg> | |
| +5.1% vs mes anterior | |
| </div> | |
| </div> | |
| <div class="kpi-card warning"> | |
| <div class="kpi-icon">📊</div> | |
| <div class="kpi-label">Ocupación Promedio</div> | |
| <div class="kpi-value" id="kpi-ocupacion"></div> | |
| <div class="kpi-trend down"> | |
| <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><path d="M12 5v14M19 12l-7 7-7-7"/></svg> | |
| -1.5% vs mes anterior | |
| </div> | |
| </div> | |
| <div class="kpi-card secondary"> | |
| <div class="kpi-icon">🎬</div> | |
| <div class="kpi-label">Total Funciones</div> | |
| <div class="kpi-value" id="kpi-funciones"></div> | |
| <div class="kpi-trend up"> | |
| <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><path d="M12 19V5M5 12l7-7 7 7"/></svg> | |
| +2.8% vs mes anterior | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Charts Grid --> | |
| <div class="grid-layout"> | |
| <div class="card col-span-2"> | |
| <div class="card-header"> | |
| <h2 class="card-title">Ranking de Ocupación por Sala</h2> | |
| <p class="card-subtitle">Porcentaje de butacas ocupadas sobre el total disponible.</p> | |
| </div> | |
| <div class="chart-container h-400"> | |
| <canvas id="ocupacionChart"></canvas> | |
| </div> | |
| </div> | |
| <div class="card col-span-1"> | |
| <div class="card-header"> | |
| <h2 class="card-title">Distribución de Espectadores</h2> | |
| <p class="card-subtitle">Aporte de cada sala al total de espectadores.</p> | |
| </div> | |
| <div class="chart-container h-400"> | |
| <canvas id="distribucionChart"></canvas> | |
| </div> | |
| </div> | |
| <div class="card table-card col-span-3"> | |
| <div class="card-header"> | |
| <h2 class="card-title">Detalle de Rendimiento por Sala</h2> | |
| <p class="card-subtitle">Análisis completo de todas las métricas por sala, ordenado por ocupación.</p> | |
| </div> | |
| <div style="overflow-x: auto;"> | |
| <table class="data-table"> | |
| <thead> | |
| <tr> | |
| <th>Rank</th> | |
| <th>Sala</th> | |
| <th>Capacidad</th> | |
| <th>Funciones</th> | |
| <th style="text-align: right;">Espectadores</th> | |
| <th>Ocupación</th> | |
| <th style="text-align: right;">Recaudación</th> | |
| </tr> | |
| </thead> | |
| <tbody id="dataTableBody"> | |
| <!-- Rows will be injected by JavaScript --> | |
| </tbody> | |
| </table> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Footer --> | |
| <footer class="footer"> | |
| Dashboard generado por <a href="https://mostachia.com" target="_blank">MostachIA</a> para Cinexo. © 2025 | |
| </footer> | |
| </div> | |
| <script> | |
| const jsonData = { | |
| "dashboardType": "ocupacion", | |
| "dashboardTitle": "Ocupación y Facturación por Sala - Septiembre 2025", | |
| "clientName": "Cinexo", | |
| "periodo": { | |
| "desde": "2025-09-01", | |
| "hasta": "2025-09-30", | |
| "descripcion": "Septiembre 2025" | |
| }, | |
| "ocupacionPorSala": [ | |
| {"sala": 4,"nombreSala": "Sala 4","capacidad": 160,"funciones": 106,"espectadores": 3279,"ocupacion": 19.33,"recaudacion": 17861500}, | |
| {"sala": 6,"nombreSala": "Sala 6","capacidad": 160,"funciones": 95,"espectadores": 2371,"ocupacion": 15.60,"recaudacion": 13167500}, | |
| {"sala": 5,"nombreSala": "Sala 5","capacidad": 160,"funciones": 95,"espectadores": 2067,"ocupacion": 13.60,"recaudacion": 11380500}, | |
| {"sala": 8,"nombreSala": "Sala 8","capacidad": 327,"funciones": 81,"espectadores": 2621,"ocupacion": 9.90,"recaudacion": 12627000}, | |
| {"sala": 7,"nombreSala": "Sala 7","capacidad": 148,"funciones": 100,"espectadores": 1091,"ocupacion": 7.37,"recaudacion": 5440000}, | |
| {"sala": 3,"nombreSala": "Sala 3","capacidad": 160,"funciones": 92,"espectadores": 994,"ocupacion": 6.75,"recaudacion": 4824000}, | |
| {"sala": 1,"nombreSala": "Sala 1","capacidad": 194,"funciones": 112,"espectadores": 1425,"ocupacion": 6.56,"recaudacion": 6920000}, | |
| {"sala": 2,"nombreSala": "Sala 2","capacidad": 133,"funciones": 86,"espectadores": 714,"ocupacion": 6.24,"recaudacion": 3561000} | |
| ] | |
| }; | |
| // === HELPERS === | |
| function formatCurrency(value) { return '$' + value.toLocaleString('es-AR', { minimumFractionDigits: 0, maximumFractionDigits: 0 }); } | |
| function formatNumber(value) { return value.toLocaleString('es-AR'); } | |
| function downloadPDF() { | |
| const element = document.getElementById('dashboard-content'); | |
| const opt = { | |
| margin: 0.5, | |
| filename: `Cinexo_Dashboard_Ocupacion_${jsonData.periodo.descripcion.replace(' ','_')}.pdf`, | |
| image: { type: 'jpeg', quality: 0.98 }, | |
| html2canvas: { scale: 2, useCORS: true }, | |
| jsPDF: { unit: 'in', format: 'letter', orientation: 'landscape' } | |
| }; | |
| html2pdf().from(element).set(opt).save(); | |
| } | |
| document.addEventListener('DOMContentLoaded', () => { | |
| // === DATA PROCESSING === | |
| const data = jsonData.ocupacionPorSala.sort((a, b) => b.ocupacion - a.ocupacion); | |
| // === FILL HEADER & KPIs === | |
| document.getElementById('dashboard-title').textContent = jsonData.dashboardTitle; | |
| document.getElementById('dashboard-subtitle').textContent = `Reporte del período: ${jsonData.periodo.descripcion}`; | |
| const totalRecaudacion = data.reduce((sum, item) => sum + item.recaudacion, 0); | |
| const totalEspectadores = data.reduce((sum, item) => sum + item.espectadores, 0); | |
| const totalFunciones = data.reduce((sum, item) => sum + item.funciones, 0); | |
| const totalCapacidadOfertada = data.reduce((sum, item) => sum + (item.capacidad * item.funciones), 0); | |
| const avgOcupacion = (totalEspectadores / totalCapacidadOfertada) * 100; | |
| document.getElementById('kpi-recaudacion').textContent = formatCurrency(totalRecaudacion); | |
| document.getElementById('kpi-espectadores').textContent = formatNumber(totalEspectadores); | |
| document.getElementById('kpi-ocupacion').textContent = `${avgOcupacion.toFixed(1)}%`; | |
| document.getElementById('kpi-funciones').textContent = formatNumber(totalFunciones); | |
| // === CHART.JS GLOBAL 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 = { | |
| primary: '#60B99A', | |
| primaryLight: '#7FCDB2', | |
| secondary: '#1F3B4D', | |
| palette: ['#60B99A', '#1F3B4D', '#FFB74D', '#8B5CF6', '#EC4899', '#06B6D4', '#84CC16', '#F59E0B'], | |
| ocupacionAlta: '#10B981', | |
| ocupacionMedia: '#F59E0B', | |
| ocupacionBaja: '#EF4444', | |
| }; | |
| // === OCUPACION BAR CHART === | |
| const ocupacionCtx = document.getElementById('ocupacionChart').getContext('2d'); | |
| new Chart(ocupacionCtx, { | |
| type: 'bar', | |
| data: { | |
| labels: data.map(d => d.nombreSala), | |
| datasets: [{ | |
| label: 'Ocupación (%)', | |
| data: data.map(d => d.ocupacion), | |
| backgroundColor: function(context) { | |
| const value = context.raw; | |
| if (value > 15) return chartColors.ocupacionAlta; | |
| if (value > 8) return chartColors.ocupacionMedia; | |
| return chartColors.ocupacionBaja; | |
| }, | |
| borderRadius: 6, | |
| borderSkipped: false, | |
| barThickness: 25, | |
| }] | |
| }, | |
| 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) => `${value.toFixed(1)}%` | |
| }, | |
| tooltip: { | |
| callbacks: { | |
| label: (ctx) => `${ctx.dataset.label}: ${ctx.raw.toFixed(2)}%` | |
| } | |
| } | |
| }, | |
| scales: { | |
| x: { | |
| beginAtZero: true, | |
| grid: { color: 'rgba(0,0,0,0.05)', drawBorder: false }, | |
| ticks: { callback: (value) => `${value}%` } | |
| }, | |
| y: { | |
| grid: { display: false }, | |
| ticks: { font: { weight: '500' } } | |
| } | |
| } | |
| } | |
| }); | |
| // === DISTRIBUCION DOUGHNUT CHART === | |
| const distribucionCtx = document.getElementById('distribucionChart').getContext('2d'); | |
| new Chart(distribucionCtx, { | |
| type: 'doughnut', | |
| data: { | |
| labels: data.map(d => d.nombreSala), | |
| datasets: [{ | |
| data: data.map(d => d.espectadores), | |
| backgroundColor: chartColors.palette, | |
| borderWidth: 0, | |
| hoverOffset: 15, | |
| hoverBorderWidth: 4, | |
| hoverBorderColor: '#fff' | |
| }] | |
| }, | |
| options: { | |
| responsive: true, | |
| maintainAspectRatio: false, | |
| cutout: '60%', | |
| plugins: { | |
| legend: { | |
| position: 'bottom', | |
| labels: { | |
| padding: 15, | |
| font: { size: 10 } | |
| } | |
| }, | |
| datalabels: { | |
| color: '#fff', | |
| font: { weight: 'bold', size: 14 }, | |
| formatter: (value, ctx) => { | |
| const total = ctx.chart.data.datasets[0].data.reduce((a, b) => a + b, 0); | |
| const percentage = ((value / total) * 100); | |
| return percentage > 7 ? percentage.toFixed(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}: ${formatNumber(value)} (${percentage}%)`; | |
| } | |
| } | |
| } | |
| } | |
| } | |
| }); | |
| // === FILL DETAILED TABLE === | |
| const tableBody = document.getElementById('dataTableBody'); | |
| data.forEach((item, index) => { | |
| const rank = index + 1; | |
| let rankHtml = ''; | |
| if (rank === 1) rankHtml = `<span class="rank-medal rank-1">1</span>`; | |
| else if (rank === 2) rankHtml = `<span class="rank-medal rank-2">2</span>`; | |
| else if (rank === 3) rankHtml = `<span class="rank-medal rank-3">3</span>`; | |
| else rankHtml = `<span class="rank-medal rank-other">${rank}</span>`; | |
| let ocupacionClass = 'low'; | |
| if (item.ocupacion > 15) ocupacionClass = 'high'; | |
| else if (item.ocupacion > 8) ocupacionClass = 'medium'; | |
| const row = ` | |
| <tr> | |
| <td>${rankHtml}</td> | |
| <td class="value-highlight">${item.nombreSala}</td> | |
| <td>${formatNumber(item.capacidad)}</td> | |
| <td>${formatNumber(item.funciones)}</td> | |
| <td style="text-align: right;">${formatNumber(item.espectadores)}</td> | |
| <td> | |
| <div class="progress-cell"> | |
| <div class="progress-bar-container"> | |
| <div class="progress-bar-fill ${ocupacionClass}" style="width: ${item.ocupacion * 4}%"></div> | |
| </div> | |
| <span class="progress-value">${item.ocupacion.toFixed(1)}%</span> | |
| </div> | |
| </td> | |
| <td class="value-highlight value-currency" style="text-align: right;">${formatCurrency(item.recaudacion)}</td> | |
| </tr> | |
| `; | |
| tableBody.innerHTML += row; | |
| }); | |
| }); | |
| </script> | |
| </body> | |
| </html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment