Spaces:
Running
Running
| <html lang="es"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Boston Scientific Dashboard</title> | |
| <link rel="stylesheet" href="https://unpkg.com/leaflet/dist/leaflet.css"/> | |
| <script src="https://unpkg.com/leaflet/dist/leaflet.js"></script> | |
| <script src="https://cdn.jsdelivr.net/npm/chart.js"></script> | |
| <style> | |
| /* [MISMO CSS QUE ANTES - OMITIDO POR BREVEDAD] */ | |
| /* Paleta de colores corporativa */ | |
| :root { | |
| --primary-blue: #0057b8; | |
| --secondary-blue: #003f7f; | |
| --light-gray: #f4f4f4; | |
| --dark-gray: #333; | |
| --highlight-yellow: #FFD700; | |
| --danger-red: #FF0000; | |
| --success-green: #008000; | |
| } | |
| /* Estilos generales */ | |
| body { | |
| font-family: Arial, sans-serif; | |
| background-color: var(--light-gray); | |
| color: var(--dark-gray); | |
| margin: 0; | |
| padding: 0; | |
| } | |
| h1 { | |
| text-align: center; | |
| color: var(--primary-blue); | |
| margin: 20px 0; | |
| } | |
| /*CAMBIO COLUMNAS*/ | |
| /* Contenedor principal con grid */ | |
| .dashboard { | |
| display: grid; | |
| grid-template-columns: 1fr 2fr 1fr;/*1fr;*/ | |
| grid-template-rows: auto auto auto; | |
| gap: 20px; | |
| padding: 20px; | |
| } | |
| /* Controles */ | |
| #controls { | |
| grid-column: 1 / -1; /* Hacer que los controles ocupen todo el ancho */ | |
| display: flex; | |
| justify-content: center; | |
| align-items: center; | |
| background: white; | |
| padding: 15px; /* Aumentado padding */ | |
| border-radius: 10px; | |
| box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); | |
| margin-bottom: 20px; /* Añadido margen inferior */ | |
| flex-wrap: wrap; /* Permitir que los controles pasen a la siguiente línea si no caben */ | |
| } | |
| #controls label { | |
| margin: 0 5px 0 15px; /* Ajuste de margen para etiquetas */ | |
| } | |
| #controls select { | |
| padding: 8px; | |
| font-size: 16px; | |
| border: 2px solid var(--primary-blue); | |
| border-radius: 5px; | |
| background: white; | |
| color: var(--dark-gray); | |
| cursor: pointer; | |
| margin: 5px; /* Añadido margen alrededor de los selectores */ | |
| } | |
| /* Estilos del mapa */ | |
| #map { | |
| height: 500px; | |
| width: 100%; | |
| border-radius: 10px; | |
| box-shadow: 0px 4px 8px rgba(0, 0, 0, 0.2); | |
| } | |
| /* Contenedor de gráficos */ | |
| .chart-container { /* Este contenedor ya no se usa directamente para los gráficos principales */ | |
| /* display: grid; | |
| grid-template-columns: repeat(2, 1fr); | |
| gap: 20px; */ | |
| } | |
| .chart-box { | |
| background: white; | |
| padding: 20px; | |
| border-radius: 10px; | |
| box-shadow: 0px 4px 8px rgba(0, 0, 0, 0.1); | |
| /* Los canvas dentro del chart-box se manejarán individualmente o por defecto */ | |
| } | |
| canvas { | |
| width: 100%; | |
| /* Altura ajustada para mejor visualización cuando hay varios */ | |
| height: auto; /* Permitir que el contenedor defina la altura o usar ratio */ | |
| max-height: 250px; /* Limitar altura máxima si es necesario */ | |
| margin-bottom: 15px; /* Espacio entre gráficos dentro del mismo chart-box */ | |
| } | |
| .chart-box canvas:last-child { | |
| margin-bottom: 0; /* Quitar margen inferior al último canvas */ | |
| } | |
| /* Responsive */ | |
| @media (max-width: 1200px) { /* Ajuste para pantallas medianas/grandes */ | |
| .dashboard { | |
| grid-template-columns: 1fr 1fr; /* 2 columnas */ | |
| grid-template-rows: auto auto auto; | |
| } | |
| #map { | |
| grid-column: 1 / -1; /* Mapa ocupa todo el ancho */ | |
| grid-row: 2; /* Mapa en la segunda fila */ | |
| } | |
| .chart-box:nth-of-type(1) { /* Primer contenedor de gráficos */ | |
| grid-column: 1 / 2; | |
| grid-row: 3; | |
| } | |
| .chart-box:nth-of-type(2) { /* Segundo contenedor de gráficos */ | |
| grid-column: 2 / 3; | |
| grid-row: 3; | |
| } | |
| #controls { | |
| grid-row: 1; /* Controles en la primera fila */ | |
| } | |
| } | |
| @media (max-width: 768px) { | |
| .dashboard { | |
| grid-template-columns: 1fr; /* 1 columna */ | |
| grid-template-rows: auto auto auto auto; /* Una fila para cada elemento principal */ | |
| } | |
| #map, .chart-box, #controls { | |
| grid-column: 1 / -1; /* Todos ocupan el ancho completo */ | |
| } | |
| #controls { grid-row: 1; } | |
| #map { grid-row: 2; } | |
| .chart-box:nth-of-type(1) { grid-row: 3; } /* Primer chart-box */ | |
| .chart-box:nth-of-type(2) { grid-row: 4; } /* Segundo chart-box */ | |
| #controls { | |
| flex-direction: column; /* Apilar controles verticalmente */ | |
| align-items: stretch; /* Estirar elementos */ | |
| } | |
| #controls label { | |
| margin: 10px 0 5px 0; /* Ajustar margen para layout vertical */ | |
| } | |
| #controls select { | |
| width: 100%; /* Ocupar todo el ancho disponible */ | |
| box-sizing: border-box; /* Incluir padding/border en el ancho */ | |
| } | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <h1>Matrix - Dashboard</h1> | |
| <!-- Controles --> | |
| <div id="controls"> | |
| <label for="category">Status:</label> | |
| <select id="category" onchange="updateMarkers()"> | |
| <option value="all">All</option> | |
| <option value="assessment">Assessment</option> | |
| <option value="deployment">In Deployment</option> | |
| <option value="operative">Operative</option> | |
| <option value="paused">Paused</option> | |
| <option value="maintenance">Maintenance</option> | |
| <option value="removed">Removed</option> | |
| </select> | |
| <label for="monthFrom">From:</label> | |
| <select id="monthFrom" onchange="updateMarkers()"> | |
| <option value="1">Jan</option> | |
| <option value="2">Feb</option> | |
| <option value="3">Mar</option> | |
| <!-- Podrías añadir más meses si creas más variables de datos --> | |
| <option value="4">Apr</option> | |
| </select> | |
| <label for="monthTo">To:</label> | |
| <select id="monthTo" onchange="updateMarkers()"> | |
| <option value="1">Jan</option> | |
| <option value="2">Feb</option> | |
| <option value="3" selected>Mar</option> <!-- Seleccionado por defecto --> | |
| <!-- Podrías añadir más meses si creas más variables de datos --> | |
| <option value="4">Apr</option> | |
| </select> | |
| </div> | |
| <!-- Dashboard Grid --> | |
| <div class="dashboard"> | |
| <div class="chart-box"> | |
| <canvas id="connectionsChart"></canvas> | |
| <canvas id="webinarChart"></canvas> | |
| <canvas id="recordingChart"></canvas> | |
| </div> | |
| <div id="map"></div> | |
| <div class="chart-box"> | |
| <canvas id="supportTimeChart"></canvas> | |
| <canvas id="webinarTime"></canvas> | |
| <canvas id="recordingTime"></canvas> | |
| </div> | |
| </div> | |
| <script> | |
| // --- DATOS MENSUALES DE EJEMPLO --- | |
| /*const locationsJan = [ | |
| { name: "Ragusa", coords: [36.9257, 14.7244], category: "operative", connections: 11, avgSupportTime: 12, webinars: 1, avgWebinarTime: 1 , recordings: 0, avgRecordingTime: 0}, | |
| { name: "Seville", coords: [37.3886, -5.9823], category: "operative", connections: 7, avgSupportTime: 2, webinars: 1, avgWebinarTime: 1 , recordings: 0, avgRecordingTime: 0}, | |
| { name: "Groningen", coords: [53.2194, 6.5665], category: "operative", connections: 1, avgSupportTime: 3, webinars: 0, avgWebinarTime: 0 , recordings: 0, avgRecordingTime: 0}, | |
| { name: "Cape Town", coords: [-33.9249, 18.4241], category: "operative", connections: 0, avgSupportTime: 0.5, webinars: 13, avgWebinarTime: 1 , recordings: 0, avgRecordingTime: 0}, | |
| { name: "Bern", coords: [46.9481, 7.4474], category: "operative", connections: 0, avgSupportTime: 0, webinars: 0, avgWebinarTime: 0 , recordings: 0, avgRecordingTime: 0}, | |
| { name: "Madrid", coords: [40.4165, -3.70256], category: "operative", connections: 0, avgSupportTime: 0, webinars: 0, avgWebinarTime: 0 , recordings: 0, avgRecordingTime: 0}, | |
| { name: "Kiel", coords: [54.3233, 10.1228], category: "removed", connections: 0, avgSupportTime: 0, webinars: 0, avgWebinarTime: 0 , recordings: 0, avgRecordingTime: 0}, | |
| { name: "Le Mans", coords: [48.0077, 0.1996], category: "assessment", connections: 0, avgSupportTime: 0, webinars: 0, avgWebinarTime: 0 , recordings: 0, avgRecordingTime: 0}, | |
| { name: "Gdansk", coords: [54.3520, 18.6466], category: "assessment", connections: 0, avgSupportTime: 0, webinars: 0, avgWebinarTime: 0 , recordings: 0, avgRecordingTime: 0}, | |
| { name: "Prague", coords: [50.0755, 14.4378], category: "assessment", connections: 0, avgSupportTime: 0, webinars: 0, avgWebinarTime: 0 , recordings: 0, avgRecordingTime: 0}, | |
| { name: "Kuwait", coords: [29.3759, 47.9774], category: "deployment", connections: 0, avgSupportTime: 0, webinars: 0, avgWebinarTime: 0 , recordings: 0, avgRecordingTime: 0} // Cambiado a deployment | |
| ]; | |
| const locationsFeb = [ | |
| { name: "Ragusa", coords: [36.9257, 14.7244], category: "operative", connections: 21, avgSupportTime: 12, webinars: 3, avgWebinarTime: 1 , recordings: 0, avgRecordingTime: 0}, | |
| { name: "Seville", coords: [37.3886, -5.9823], category: "operative", connections: 5, avgSupportTime: 2, webinars: 6, avgWebinarTime: 1 , recordings: 0, avgRecordingTime: 0}, | |
| { name: "Groningen", coords: [53.2194, 6.5665], category: "operative", connections: 13, avgSupportTime: 3, webinars: 0, avgWebinarTime: 0 , recordings: 0, avgRecordingTime: 0}, | |
| { name: "Cape Town", coords: [-33.9249, 18.4241], category: "operative", connections: 13, avgSupportTime: 0.5, webinars: 0, avgWebinarTime: 1 , recordings: 0, avgRecordingTime: 0}, | |
| { name: "Bern", coords: [46.9481, 7.4474], category: "operative", connections: 0, avgSupportTime: 0, webinars: 0, avgWebinarTime: 0 , recordings: 0, avgRecordingTime: 0}, // Cambiado a maintenance | |
| { name: "Madrid", coords: [40.4165, -3.70256], category: "operative", connections: 0, avgSupportTime: 0, webinars: 0, avgWebinarTime: 0 , recordings: 0, avgRecordingTime: 0}, | |
| { name: "Kiel", coords: [54.3233, 10.1228], category: "removed", connections: 0, avgSupportTime: 0, webinars: 0, avgWebinarTime: 0 , recordings: 0, avgRecordingTime: 0}, | |
| { name: "Le Mans", coords: [48.0077, 0.1996], category: "assessment", connections: 0, avgSupportTime: 0, webinars: 0, avgWebinarTime: 0 , recordings: 0, avgRecordingTime: 0}, | |
| { name: "Gdansk", coords: [54.3520, 18.6466], category: "assessment", connections: 0, avgSupportTime: 0, webinars: 0, avgWebinarTime: 0 , recordings: 0, avgRecordingTime: 0}, // Cambiado a deployment | |
| { name: "Prague", coords: [50.0755, 14.4378], category: "assessment", connections: 0, avgSupportTime: 0, webinars: 0, avgWebinarTime: 0 , recordings: 0, avgRecordingTime: 0}, | |
| { name: "Kuwait", coords: [29.3759, 47.9774], category: "assessment", connections: 0, avgSupportTime: 0, webinars: 0, avgWebinarTime: 0 , recordings: 0, avgRecordingTime: 0} | |
| ]; | |
| const locationsMar = [ | |
| { name: "Ragusa", coords: [36.9257, 14.7244], category: "operative", connections: 13, avgSupportTime: 12, webinars: 0, avgWebinarTime: 1 , recordings: 0, avgRecordingTime: 0.5}, // Añadida grabación | |
| { name: "Seville", coords: [37.3886, -5.9823], category: "operative", connections: 5, avgSupportTime: 2, webinars: 0, avgWebinarTime: 1 , recordings: 0, avgRecordingTime: 0}, | |
| { name: "Groningen", coords: [53.2194, 6.5665], category: "operative", connections: 3, avgSupportTime: 3, webinars: 0, avgWebinarTime: 0 , recordings: 0, avgRecordingTime: 0}, | |
| { name: "Cape Town", coords: [-33.9249, 18.4241], category: "operative", connections: 1, avgSupportTime: 0.5, webinars: 0, avgWebinarTime: 1 , recordings: 0, avgRecordingTime: 0}, // Cambiado a paused | |
| { name: "Bern", coords: [46.9481, 7.4474], category: "operative", connections: 3, avgSupportTime: 0, webinars: 0, avgWebinarTime: 0 , recordings: 0, avgRecordingTime: 0}, // Vuelve a operative | |
| { name: "Madrid", coords: [40.4165, -3.70256], category: "operative", connections: 0, avgSupportTime: 0, webinars: 0, avgWebinarTime: 0 , recordings: 0, avgRecordingTime: 0}, | |
| { name: "Kiel", coords: [54.3233, 10.1228], category: "removed", connections: 0, avgSupportTime: 0, webinars: 0, avgWebinarTime: 0 , recordings: 0, avgRecordingTime: 0}, | |
| { name: "Le Mans", coords: [48.0077, 0.1996], category: "assessment", connections: 0, avgSupportTime: 0, webinars: 0, avgWebinarTime: 0 , recordings: 0, avgRecordingTime: 0}, | |
| { name: "Gdansk", coords: [54.3520, 18.6466], category: "assessment", connections: 0, avgSupportTime: 0, webinars: 0, avgWebinarTime: 0 , recordings: 0, avgRecordingTime: 0}, | |
| { name: "Prague", coords: [50.0755, 14.4378], category: "assessment", connections: 0, avgSupportTime: 0, webinars: 0, avgWebinarTime: 0 , recordings: 0, avgRecordingTime: 0}, | |
| { name: "Kuwait", coords: [29.3759, 47.9774], category: "assessment", connections: 0, avgSupportTime: 0, webinars: 0, avgWebinarTime: 0 , recordings: 0, avgRecordingTime: 0} // Pasa a operative con datos | |
| ];*/ | |
| const locationsJan = [ | |
| { name: "Ragusa", coords: [36.9257, 14.7244], category: "operative", connections: 11, avgSupportTime: 1, webinars: 1, avgWebinarTime: 1 , recordings: 0, avgRecordingTime: 0}, | |
| { name: "Seville", coords: [37.3886, -5.9823], category: "operative", connections: 7, avgSupportTime: 0.5, webinars: 1, avgWebinarTime: 1 , recordings: 0, avgRecordingTime: 0}, | |
| { name: "Groningen", coords: [53.2194, 6.5665], category: "operative", connections: 1, avgSupportTime: 3, webinars: 0, avgWebinarTime: 0 , recordings: 0, avgRecordingTime: 0}, | |
| { name: "Cape Town", coords: [-33.9249, 18.4241], category: "operative", connections: 0, avgSupportTime: 0.5, webinars: 13, avgWebinarTime: 1 , recordings: 0, avgRecordingTime: 0}, | |
| { name: "Bern", coords: [46.9481, 7.4474], category: "operative", connections: 0, avgSupportTime: 0, webinars: 0, avgWebinarTime: 0 , recordings: 0, avgRecordingTime: 0}, | |
| { name: "Madrid", coords: [40.4165, -3.70256], category: "operative", connections: 17, avgSupportTime: 1.50, webinars: 5, avgWebinarTime: 1 , recordings: 1, avgRecordingTime: 0.05}, | |
| { name: "Kiel", coords: [54.3233, 10.1228], category: "removed", connections: 0, avgSupportTime: 0, webinars: 0, avgWebinarTime: 0 , recordings: 0, avgRecordingTime: 0}, | |
| { name: "Le Mans", coords: [48.0077, 0.1996], category: "assessment", connections: 0, avgSupportTime: 0, webinars: 0, avgWebinarTime: 0 , recordings: 0, avgRecordingTime: 0}, | |
| { name: "Gdansk", coords: [54.3520, 18.6466], category: "assessment", connections: 0, avgSupportTime: 0, webinars: 0, avgWebinarTime: 0 , recordings: 0, avgRecordingTime: 0}, | |
| { name: "Prague", coords: [50.0755, 14.4378], category: "assessment", connections: 0, avgSupportTime: 0, webinars: 0, avgWebinarTime: 0 , recordings: 0, avgRecordingTime: 0}, | |
| { name: "Kuwait", coords: [29.3759, 47.9774], category: "deployment", connections: 0, avgSupportTime: 0, webinars: 0, avgWebinarTime: 0 , recordings: 0, avgRecordingTime: 0} // Cambiado a deployment | |
| ]; | |
| const locationsFeb = [ | |
| { name: "Ragusa", coords: [36.9257, 14.7244], category: "operative", connections: 21, avgSupportTime: 2, webinars: 3, avgWebinarTime: 1 , recordings: 0, avgRecordingTime: 0}, | |
| { name: "Seville", coords: [37.3886, -5.9823], category: "operative", connections: 5, avgSupportTime: 0.5, webinars: 6, avgWebinarTime: 1 , recordings: 0, avgRecordingTime: 0}, | |
| { name: "Groningen", coords: [53.2194, 6.5665], category: "operative", connections: 13, avgSupportTime: 3, webinars: 0, avgWebinarTime: 0 , recordings: 0, avgRecordingTime: 0}, | |
| { name: "Cape Town", coords: [-33.9249, 18.4241], category: "operative", connections: 13, avgSupportTime: 0.5, webinars: 0, avgWebinarTime: 1 , recordings: 0, avgRecordingTime: 0}, | |
| { name: "Bern", coords: [46.9481, 7.4474], category: "operative", connections: 0, avgSupportTime: 0, webinars: 0, avgWebinarTime: 0 , recordings: 0, avgRecordingTime: 0}, // Cambiado a maintenance | |
| { name: "Madrid", coords: [40.4165, -3.70256], category: "operative", connections: 25, avgSupportTime: 0.23, webinars: 1, avgWebinarTime: 1 , recordings: 1, avgRecordingTime: 0.10}, | |
| { name: "Kiel", coords: [54.3233, 10.1228], category: "removed", connections: 0, avgSupportTime: 0, webinars: 0, avgWebinarTime: 0 , recordings: 0, avgRecordingTime: 0}, | |
| { name: "Le Mans", coords: [48.0077, 0.1996], category: "assessment", connections: 0, avgSupportTime: 0, webinars: 0, avgWebinarTime: 0 , recordings: 0, avgRecordingTime: 0}, | |
| { name: "Gdansk", coords: [54.3520, 18.6466], category: "assessment", connections: 0, avgSupportTime: 0, webinars: 0, avgWebinarTime: 0 , recordings: 0, avgRecordingTime: 0}, // Cambiado a deployment | |
| { name: "Prague", coords: [50.0755, 14.4378], category: "assessment", connections: 0, avgSupportTime: 0, webinars: 0, avgWebinarTime: 0 , recordings: 0, avgRecordingTime: 0}, | |
| { name: "Kuwait", coords: [29.3759, 47.9774], category: "assessment", connections: 0, avgSupportTime: 0, webinars: 0, avgWebinarTime: 0 , recordings: 0, avgRecordingTime: 0} | |
| ]; | |
| const locationsMar = [ | |
| { name: "Ragusa", coords: [36.9257, 14.7244], category: "operative", connections: 13, avgSupportTime: 1, webinars: 0, avgWebinarTime: 1 , recordings: 0, avgRecordingTime: 0.5}, // Añadida grabación | |
| { name: "Seville", coords: [37.3886, -5.9823], category: "operative", connections: 5, avgSupportTime: 0.5, webinars: 0, avgWebinarTime: 1 , recordings: 0, avgRecordingTime: 0}, | |
| { name: "Groningen", coords: [53.2194, 6.5665], category: "operative", connections: 3, avgSupportTime: 1, webinars: 0, avgWebinarTime: 0 , recordings: 0, avgRecordingTime: 0}, | |
| { name: "Cape Town", coords: [-33.9249, 18.4241], category: "operative", connections: 1, avgSupportTime: 0.5, webinars: 0, avgWebinarTime: 1 , recordings: 0, avgRecordingTime: 0}, // Cambiado a paused | |
| { name: "Bern", coords: [46.9481, 7.4474], category: "operative", connections: 3, avgSupportTime: 0, webinars: 0, avgWebinarTime: 0 , recordings: 0, avgRecordingTime: 0}, // Vuelve a operative | |
| { name: "Madrid", coords: [40.4165, -3.70256], category: "operative", connections: 17, avgSupportTime: 0.32, webinars: 0, avgWebinarTime: 0 , recordings: 0, avgRecordingTime: 0}, | |
| { name: "Kiel", coords: [54.3233, 10.1228], category: "removed", connections: 0, avgSupportTime: 0, webinars: 0, avgWebinarTime: 0 , recordings: 0, avgRecordingTime: 0}, | |
| { name: "Le Mans", coords: [48.0077, 0.1996], category: "assessment", connections: 0, avgSupportTime: 0, webinars: 0, avgWebinarTime: 0 , recordings: 0, avgRecordingTime: 0}, | |
| { name: "Gdansk", coords: [54.3520, 18.6466], category: "assessment", connections: 0, avgSupportTime: 0, webinars: 0, avgWebinarTime: 0 , recordings: 0, avgRecordingTime: 0}, | |
| { name: "Prague", coords: [50.0755, 14.4378], category: "assessment", connections: 0, avgSupportTime: 0, webinars: 0, avgWebinarTime: 0 , recordings: 0, avgRecordingTime: 0}, | |
| { name: "Kuwait", coords: [29.3759, 47.9774], category: "assessment", connections: 0, avgSupportTime: 0, webinars: 0, avgWebinarTime: 0 , recordings: 0, avgRecordingTime: 0} // Pasa a operative con datos | |
| ]; | |
| const locationsApr = [ | |
| { name: "Ragusa", coords: [36.9257, 14.7244], category: "operative", connections: 8, avgSupportTime: 4, webinars: 0, avgWebinarTime: 1 , recordings: 0, avgRecordingTime: 0.5}, // Añadida grabación | |
| { name: "Seville", coords: [37.3886, -5.9823], category: "operative", connections: 4, avgSupportTime: 1.5, webinars: 0, avgWebinarTime: 1 , recordings: 0, avgRecordingTime: 0}, | |
| { name: "Groningen", coords: [53.2194, 6.5665], category: "operative", connections: 5, avgSupportTime: 1.7, webinars: 4, avgWebinarTime: 1 , recordings: 0, avgRecordingTime: 0}, | |
| { name: "Cape Town", coords: [-33.9249, 18.4241], category: "operative", connections: 4, avgSupportTime: 0.5, webinars: 0, avgWebinarTime: 1 , recordings: 0, avgRecordingTime: 0}, // Cambiado a paused | |
| { name: "Bern", coords: [46.9481, 7.4474], category: "operative", connections: 3, avgSupportTime: 1, webinars: 0, avgWebinarTime: 0 , recordings: 0, avgRecordingTime: 0}, // Vuelve a operative | |
| { name: "Madrid", coords: [40.4165, -3.70256], category: "operative", connections: 12, avgSupportTime: 0.5, webinars: 0, avgWebinarTime: 0 , recordings: 0, avgRecordingTime: 0}, | |
| { name: "Kiel", coords: [54.3233, 10.1228], category: "removed", connections: 0, avgSupportTime: 0, webinars: 0, avgWebinarTime: 0 , recordings: 0, avgRecordingTime: 0}, | |
| { name: "Le Mans", coords: [48.0077, 0.1996], category: "assessment", connections: 0, avgSupportTime: 0, webinars: 0, avgWebinarTime: 0 , recordings: 0, avgRecordingTime: 0}, | |
| { name: "Gdansk", coords: [54.3520, 18.6466], category: "assessment", connections: 0, avgSupportTime: 0, webinars: 0, avgWebinarTime: 0 , recordings: 0, avgRecordingTime: 0}, | |
| { name: "Prague", coords: [50.0755, 14.4378], category: "assessment", connections: 0, avgSupportTime: 0, webinars: 0, avgWebinarTime: 0 , recordings: 0, avgRecordingTime: 0}, | |
| { name: "Kuwait", coords: [29.3759, 47.9774], category: "assessment", connections: 0, avgSupportTime: 0, webinars: 0, avgWebinarTime: 0 , recordings: 0, avgRecordingTime: 0}, // Pasa a operative con datos | |
| { name: "Şalālah", coords: [17.01505, 54.09237], category: "operative", connections: 0, avgSupportTime: 0, webinars: 0, avgWebinarTime: 0 , recordings: 0, avgRecordingTime: 0} // Pasa a operative con datos | |
| ]; | |
| // Mapa y colores (sin cambios) | |
| var map = L.map('map').setView([50.0755, 14.4378], 3); | |
| L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { | |
| attribution: 'Map data © <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors' | |
| }).addTo(map); | |
| var categoryColors = { /* ... Mismos colores ... */ | |
| "assessment": "#0057b8", | |
| "deployment": "#ff8c00", // Naranja para deployment | |
| "operative": "#008000", | |
| "paused": "#FFD700", | |
| "maintenance": "#FFA500", | |
| "removed": "#FF0000" | |
| }; | |
| var markers = []; | |
| var chart1 = null, chart2 = null, chart3 = null, chart4 = null, chart5 = null, chart6 = null; | |
| /** | |
| * Agrega datos de múltiples datasets mensuales. | |
| * @param {Array<Array<Object>>} datasets - Array de datasets (ej: [locationsJan, locationsFeb]). | |
| * @returns {Array<Object>} - Un único dataset con valores agregados. | |
| */ | |
| function aggregateMonthlyData(datasets) { | |
| const aggregated = {}; // Usar objeto para agrupar por nombre | |
| datasets.forEach(monthlyData => { | |
| monthlyData.forEach(location => { | |
| const name = location.name; | |
| if (!aggregated[name]) { | |
| // Si es la primera vez que vemos esta ubicación, inicializarla | |
| aggregated[name] = { | |
| ...location, // Copia coords, category (la del primer mes encontrado) | |
| totalConnections: 0, | |
| totalSupportTime: 0, | |
| totalWebinars: 0, | |
| totalWebinarTime: 0, | |
| totalRecordings: 0, | |
| totalRecordingTime: 0, | |
| }; | |
| // Usar la categoría del *último* mes en el rango como la representativa | |
| if (datasets.length > 0) { | |
| const lastMonthData = datasets[datasets.length - 1]; | |
| const lastLocationState = lastMonthData.find(loc => loc.name === name); | |
| if (lastLocationState) { | |
| aggregated[name].category = lastLocationState.category; | |
| } | |
| } | |
| } | |
| // Acumular totales | |
| aggregated[name].totalConnections += location.connections; | |
| aggregated[name].totalSupportTime += location.connections * location.avgSupportTime; // Tiempo total = conexiones * promedio | |
| aggregated[name].totalWebinars += location.webinars; | |
| aggregated[name].totalWebinarTime += location.webinars * location.avgWebinarTime; | |
| aggregated[name].totalRecordings += location.recordings; | |
| aggregated[name].totalRecordingTime += location.recordings * location.avgRecordingTime; | |
| }); | |
| }); | |
| // Calcular promedios finales y formatear salida | |
| const finalData = Object.values(aggregated).map(loc => { | |
| // Evitar división por cero | |
| const avgSupportTime = loc.totalConnections > 0 ? (loc.totalSupportTime / loc.totalConnections) : 0; | |
| const avgWebinarTime = loc.totalWebinars > 0 ? (loc.totalWebinarTime / loc.totalWebinars) : 0; | |
| const avgRecordingTime = loc.totalRecordings > 0 ? (loc.totalRecordingTime / loc.totalRecordings) : 0; | |
| return { | |
| ...loc, // Mantiene name, coords, category | |
| connections: loc.totalConnections, | |
| avgSupportTime: parseFloat(avgSupportTime.toFixed(2)), // Redondear a 2 decimales | |
| webinars: loc.totalWebinars, | |
| avgWebinarTime: parseFloat(avgWebinarTime.toFixed(2)), | |
| recordings: loc.totalRecordings, | |
| avgRecordingTime: parseFloat(avgRecordingTime.toFixed(2)), | |
| // Eliminar propiedades temporales si se desea | |
| // totalConnections, totalSupportTime, etc. ya no son necesarias | |
| }; | |
| }); | |
| return finalData; | |
| } | |
| function updateMarkers() { | |
| var selectedCategory = document.getElementById("category").value; | |
| var selectedMonthFrom = parseInt(document.getElementById("monthFrom").value, 10); | |
| var selectedMonthTo = parseInt(document.getElementById("monthTo").value, 10); | |
| let datasetsToCombine = []; | |
| // Validar rango básico | |
| //if (selectedMonthFrom > selectedMonthTo) { | |
| // console.warn("El mes 'From' no puede ser posterior al mes 'To'. Mostrando datos solo para 'From'."); | |
| // selectedMonthTo = selectedMonthFrom; // O mostrar un error/nada | |
| //} | |
| if (selectedMonthFrom > selectedMonthTo) { | |
| alert("Error: El mes 'From' no puede ser posterior al mes 'To'. Por favor, corrija la selección."); | |
| return; // Detiene la ejecución de la función aquí | |
| } | |
| // Determinar qué datasets incluir basándose en el rango (solo para meses 1, 2, 3) | |
| if (selectedMonthFrom <= 1 && selectedMonthTo >= 1) datasetsToCombine.push(locationsJan); | |
| if (selectedMonthFrom <= 2 && selectedMonthTo >= 2) datasetsToCombine.push(locationsFeb); | |
| if (selectedMonthFrom <= 3 && selectedMonthTo >= 3) datasetsToCombine.push(locationsMar); | |
| if (selectedMonthFrom <= 4 && selectedMonthTo >= 4) datasetsToCombine.push(locationsApr); | |
| // Añadir más 'if' si se crean locationsApr, locationsMay, etc. | |
| let dataToDisplay = []; | |
| if (datasetsToCombine.length > 0) { | |
| // Si hay más de un mes, agregar. Si solo hay uno, aggregateMonthlyData lo manejará. | |
| dataToDisplay = aggregateMonthlyData(datasetsToCombine); | |
| } else { | |
| console.log("No hay datos disponibles para el rango de meses seleccionado (1-3)."); | |
| } | |
| // --- Filtrado por Categoría (se aplica DESPUÉS de agregar por mes) --- | |
| let filteredLocations = dataToDisplay.filter(location => | |
| selectedCategory === "all" || location.category === selectedCategory | |
| ); | |
| console.log(`Mostrando datos agregados de Mes ${selectedMonthFrom} a ${selectedMonthTo}. Categoría: ${selectedCategory}. Ubicaciones filtradas: ${filteredLocations.length}`); | |
| // --- Actualización de Mapa y Gráficos (sin cambios internos, solo usan filteredLocations) --- | |
| markers.forEach(marker => map.removeLayer(marker)); | |
| markers = []; | |
| filteredLocations.forEach(location => { | |
| // Usar la categoría (posiblemente actualizada por la agregación) | |
| const color = categoryColors[location.category] || '#CCCCCC'; | |
| var marker = L.circleMarker(location.coords, { | |
| color: color, | |
| fillColor: color, | |
| fillOpacity: 0.8, | |
| radius: 8 | |
| }).bindPopup(`<b>${location.name}</b><br>Categoría: ${location.category}<br>Conexiones: ${location.connections}<br>Soporte: ${location.avgSupportTime} hrs`); | |
| marker.addTo(map); | |
| markers.push(marker); | |
| }); | |
| // Ajustar vista del mapa | |
| if (markers.length > 1) { | |
| var group = new L.featureGroup(markers); | |
| try { map.fitBounds(group.getBounds().pad(0.3)); } catch (e) { /*...*/ } | |
| } else if (markers.length === 1) { | |
| map.setView(markers[0].getLatLng(), 6); | |
| } else { | |
| map.setView([48, 15], 4); | |
| } | |
| // Actualizar gráficos | |
| updateCharts(filteredLocations); | |
| } | |
| // --- La función updateCharts permanece sin cambios --- | |
| function updateCharts(filteredLocations) { | |
| // ... (código idéntico al de la versión anterior para crear/actualizar los 6 gráficos) ... | |
| var cityNames = filteredLocations.map(location => location.name); | |
| var connectionsData = filteredLocations.map(location => location.connections); | |
| var avgSupportTimeData = filteredLocations.map(location => location.avgSupportTime); | |
| var webinarData = filteredLocations.map(location => location.webinars); | |
| var avgWebinarTimeData = filteredLocations.map(location => location.avgWebinarTime); | |
| var recordingData = filteredLocations.map(location => location.recordings); | |
| var avgRecordingTimeData = filteredLocations.map(location => location.avgRecordingTime); | |
| // Usar el color de la categoría final (después de la agregación) | |
| var categoryColorsData = filteredLocations.map(location => categoryColors[location.category] || '#CCCCCC'); | |
| // Destruir gráficos existentes | |
| if (chart1) chart1.destroy(); | |
| if (chart2) chart2.destroy(); | |
| if (chart3) chart3.destroy(); | |
| if (chart4) chart4.destroy(); | |
| if (chart5) chart5.destroy(); | |
| if (chart6) chart6.destroy(); | |
| // --- Opciones comunes para gráficos (reutilizadas de la versión anterior) --- | |
| var commonOptions = (titleText) => ({ | |
| responsive: true, | |
| maintainAspectRatio: false, | |
| plugins: { legend: { display: false }, title: { display: true, text: titleText, font: { size: 14 }, color: '#333', padding: { top: 5, bottom: 15 } } }, | |
| scales: { x: { ticks: { color: '#333', font: {size: 10} }, grid: { display: false } }, y: { beginAtZero: true, ticks: { color: '#333' }, grid: { color: 'rgba(0,0,0,0.05)' } } }, | |
| animation: { duration: 800, easing: 'easeOutQuad' } | |
| }); | |
| var commonDatasetOptions = (data, colors) => ({ | |
| data: data, backgroundColor: colors, borderColor: 'rgba(51, 51, 51, 0.5)', borderWidth: 1, borderRadius: 5, hoverBackgroundColor: 'rgba(0, 0, 0, 0.2)' | |
| }); | |
| // --- Creación de Gráficos --- | |
| var ctx1 = document.getElementById('connectionsChart').getContext('2d'); | |
| chart1 = new Chart(ctx1, { type: 'bar', data: { labels: cityNames, datasets: [commonDatasetOptions(connectionsData, categoryColorsData)] }, options: commonOptions('Total Remote connections (Selected Period)') }); | |
| var ctx2 = document.getElementById('supportTimeChart').getContext('2d'); | |
| chart2 = new Chart(ctx2, { type: 'bar', data: { labels: cityNames, datasets: [commonDatasetOptions(avgSupportTimeData, categoryColorsData)] }, options: commonOptions('Average Support Time (hrs/connection, Selected Period)') }); // Título ajustado | |
| var ctx3 = document.getElementById('webinarChart').getContext('2d'); | |
| chart3 = new Chart(ctx3, { type: 'bar', data: { labels: cityNames, datasets: [commonDatasetOptions(webinarData, categoryColorsData)] }, options: commonOptions('Total Webinar Connections (Selected Period)') }); | |
| var ctx4 = document.getElementById('webinarTime').getContext('2d'); | |
| chart4 = new Chart(ctx4, { type: 'bar', data: { labels: cityNames, datasets: [commonDatasetOptions(avgWebinarTimeData, categoryColorsData)] }, options: commonOptions('Average Webinar Time (hrs/session, Selected Period)') }); // Título ajustado | |
| var ctx5 = document.getElementById('recordingChart').getContext('2d'); | |
| chart5 = new Chart(ctx5, { type: 'bar', data: { labels: cityNames, datasets: [commonDatasetOptions(recordingData, categoryColorsData)] }, options: commonOptions('Total Recordings (Selected Period)') }); | |
| var ctx6 = document.getElementById('recordingTime').getContext('2d'); | |
| chart6 = new Chart(ctx6, { type: 'bar', data: { labels: cityNames, datasets: [commonDatasetOptions(avgRecordingTimeData, categoryColorsData)] }, options: commonOptions('Average Recording Time (hrs/recording, Selected Period)') }); // Título ajustado | |
| } | |
| // Llamada inicial | |
| document.addEventListener('DOMContentLoaded', function() { | |
| // Establecer mes 'To' por defecto (ej: Marzo = 3) | |
| const monthToSelect = document.getElementById('monthTo'); | |
| monthToSelect.value = 3; | |
| // Establecer mes 'From' por defecto (ej: Enero = 1) | |
| const monthFromSelect = document.getElementById('monthFrom'); | |
| monthFromSelect.value = 1; | |
| updateMarkers(); // Carga inicial con el rango por defecto (Jan-Mar) | |
| }); | |
| </script> | |
| </body> | |
| </html> | |