JPLTedCas commited on
Commit
4b7a3ab
·
verified ·
1 Parent(s): 0db46c2

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +307 -371
index.html CHANGED
@@ -8,8 +8,9 @@
8
  <script src="https://unpkg.com/leaflet/dist/leaflet.js"></script>
9
  <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
10
  <style>
11
- /* Paleta de colores corporativa */
12
- :root {
 
13
  --primary-blue: #0057b8;
14
  --secondary-blue: #003f7f;
15
  --light-gray: #f4f4f4;
@@ -35,7 +36,7 @@
35
  }
36
 
37
 
38
- /*CAMBIO COLUMNAS*/
39
 
40
  /* Contenedor principal con grid */
41
  .dashboard {
@@ -48,16 +49,23 @@
48
 
49
  /* Controles */
50
  #controls {
 
51
  display: flex;
52
  justify-content: center;
53
  align-items: center;
54
  background: white;
55
- padding: 10px;
56
  border-radius: 10px;
57
  box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
 
 
58
  }
59
 
60
- select {
 
 
 
 
61
  padding: 8px;
62
  font-size: 16px;
63
  border: 2px solid var(--primary-blue);
@@ -65,6 +73,7 @@
65
  background: white;
66
  color: var(--dark-gray);
67
  cursor: pointer;
 
68
  }
69
 
70
  /* Estilos del mapa */
@@ -76,10 +85,10 @@
76
  }
77
 
78
  /* Contenedor de gráficos */
79
- .chart-container {
80
- display: grid;
81
  grid-template-columns: repeat(2, 1fr);
82
- gap: 20px;
83
  }
84
 
85
  .chart-box {
@@ -87,29 +96,76 @@
87
  padding: 20px;
88
  border-radius: 10px;
89
  box-shadow: 0px 4px 8px rgba(0, 0, 0, 0.1);
90
- /*NUEVO*/
91
- /*height: 100px;*/ /* Aumenta la altura del contenedor */
92
- /*display: flex;
93
- justify-content: center;
94
- align-items: center;*/
95
  }
96
 
97
  canvas {
98
  width: 100%;
99
- height: 300px;
 
 
 
100
  }
 
 
 
 
101
 
102
  /* Responsive */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
103
  @media (max-width: 768px) {
104
- .chart-container {
105
- grid-template-columns: 1fr;
 
106
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
107
  }
108
  </style>
109
  </head>
110
  <body>
111
 
112
  <h1>Matrix - Dashboard</h1>
 
 
113
  <div id="controls">
114
  <label for="category">Status:</label>
115
  <select id="category" onchange="updateMarkers()">
@@ -121,428 +177,308 @@
121
  <option value="maintenance">Maintenance</option>
122
  <option value="removed">Removed</option>
123
  </select>
124
- </div>
125
- <div class="dashboard">
126
-
127
 
128
- <!-- <div class="dashboard">
129
- <div id="controls">...</div>
130
- <div class="chart-box">
131
- <canvas id="connectionsChart"></canvas>
132
- </div>
133
- <div id="map"></div>
134
- <div class="chart-box">
135
- <canvas id="supportTimeChart"></canvas>
136
- </div>
137
- </div> -->
138
 
 
 
 
 
 
 
 
 
 
 
139
 
 
 
 
 
 
 
 
 
 
140
 
 
 
141
  <div class="chart-box">
142
  <canvas id="connectionsChart"></canvas>
143
  <canvas id="webinarChart"></canvas>
144
  <canvas id="recordingChart"></canvas>
145
  </div>
146
-
147
-
148
  <div id="map"></div>
149
-
150
  <div class="chart-box">
151
  <canvas id="supportTimeChart"></canvas>
152
  <canvas id="webinarTime"></canvas>
153
  <canvas id="recordingTime"></canvas>
154
  </div>
155
-
156
- <!-- <div class="chart-container">
157
- <div class="chart-box">
158
- <canvas id="connectionsChart"></canvas>
159
- </div>
160
- <div class="chart-box">
161
- <canvas id="supportTimeChart"></canvas>
162
- </div>
163
- </div> -->
164
  </div>
165
-
166
 
167
-
168
  <script>
169
- //Dec24-Feb25
170
- /* var locations = [
171
- { name: "Ragusa", coords: [36.9257, 14.7244], category: "operative", connections: 12, avgSupportTime: 12, webinars: 1, avgWebinarTime: 1 , recordings: 0, avgRecordingTime: 0}, // Ragusa, Italia
172
- { name: "Seville", coords: [37.3886, -5.9823], category: "operative", connections: 5, avgSupportTime: 1, webinars: 2, avgWebinarTime: 1 , recordings: 0, avgRecordingTime: 0}, // Sevilla, España
173
- { name: "Groningen", coords: [53.2194, 6.5665], category: "operative", connections: 7, avgSupportTime: 3, webinars: 0, avgWebinarTime: 0 , recordings: 0, avgRecordingTime: 0}, // Groningen, Países Bajos
174
- { name: "Cape Town", coords: [-33.9249, 18.4241], category: "operative", connections: 1, avgSupportTime: 0, webinars: 7, avgWebinarTime: 1 , recordings: 0, avgRecordingTime: 0}, // Ciudad del Cabo, Sudáfrica
175
- { name: "Bern", coords: [46.9481, 7.4474], category: "operative", connections: 0, avgSupportTime: 0, webinars: 0, avgWebinarTime: 0 , recordings: 0, avgRecordingTime: 0}, // Berna, Suiza
176
- { name: "Kiel", coords: [54.3233, 10.1228], category: "removed", connections: 0, avgSupportTime: 0, webinars: 0, avgWebinarTime: 0 , recordings: 0, avgRecordingTime: 0}, // Kiel, Alemania
177
- { name: "Le Mans", coords: [17.0151, 54.0924], category: "assessment", connections: 0, avgSupportTime: 0, webinars: 0, avgWebinarTime: 0 , recordings: 0, avgRecordingTime: 0}, // Salalah, Omán
178
- { name: "Le Mans", coords: [48.0077, 0.1996], category: "assessment", connections: 0, avgSupportTime: 0, webinars: 0, avgWebinarTime: 0 , recordings: 0, avgRecordingTime: 0}, // Le Mans, Francia
179
- { name: "Gdansk", coords: [54.3520, 18.6466], category: "assessment", connections: 0, avgSupportTime: 0, webinars: 0, avgWebinarTime: 0 , recordings: 0, avgRecordingTime: 0}, // Gdansk, Polonia
180
- { name: "Prague", coords: [50.0755, 14.4378], category: "assessment", connections: 0, avgSupportTime: 0, webinars: 0, avgWebinarTime: 0 , recordings: 0, avgRecordingTime: 0}, // Praga, República Checa
181
- { name: "Kuwait", coords: [29.3759, 47.9774], category: "assessment", connections: 0, avgSupportTime: 0, webinars: 0, avgWebinarTime: 0 , recordings: 0, avgRecordingTime: 0} // Kuwait, Kuwait
182
- ];*/
183
- //Dec24-March25
184
- var locations = [
185
- { name: "Ragusa", coords: [36.9257, 14.7244], category: "operative", connections: 12, avgSupportTime: 12, webinars: 4, avgWebinarTime: 1 , recordings: 0, avgRecordingTime: 0}, // Ragusa, Italia
186
- { name: "Seville", coords: [37.3886, -5.9823], category: "operative", connections: 6, avgSupportTime: 2, webinars: 7, avgWebinarTime: 1 , recordings: 0, avgRecordingTime: 0}, // Sevilla, España
187
- { name: "Groningen", coords: [53.2194, 6.5665], category: "operative", connections: 6, avgSupportTime: 3, webinars: 0, avgWebinarTime: 0 , recordings: 0, avgRecordingTime: 0}, // Groningen, Países Bajos
188
- { name: "Cape Town", coords: [-33.9249, 18.4241], category: "operative", connections: 5, avgSupportTime: 0.5, webinars: 13, avgWebinarTime: 1 , recordings: 0, avgRecordingTime: 0}, // Ciudad del Cabo, Sudáfrica
189
- { name: "Bern", coords: [46.9481, 7.4474], category: "operative", connections: 1, avgSupportTime: 0, webinars: 0, avgWebinarTime: 0 , recordings: 0, avgRecordingTime: 0}, // Berna, Suiza
190
- { name: "Kiel", coords: [54.3233, 10.1228], category: "removed", connections: 0, avgSupportTime: 0, webinars: 0, avgWebinarTime: 0 , recordings: 0, avgRecordingTime: 0}, // Kiel, Alemania
191
- //{ name: "Le Mans", coords: [17.0151, 54.0924], category: "assessment", connections: 0, avgSupportTime: 0, webinars: 0, avgWebinarTime: 0 , recordings: 0, avgRecordingTime: 0}, // Salalah, Omán
192
- { name: "Le Mans", coords: [48.0077, 0.1996], category: "assessment", connections: 0, avgSupportTime: 0, webinars: 0, avgWebinarTime: 0 , recordings: 0, avgRecordingTime: 0}, // Le Mans, Francia
193
- { name: "Gdansk", coords: [54.3520, 18.6466], category: "assessment", connections: 0, avgSupportTime: 0, webinars: 0, avgWebinarTime: 0 , recordings: 0, avgRecordingTime: 0}, // Gdansk, Polonia
194
- { name: "Prague", coords: [50.0755, 14.4378], category: "assessment", connections: 0, avgSupportTime: 0, webinars: 0, avgWebinarTime: 0 , recordings: 0, avgRecordingTime: 0}, // Praga, República Checa
195
- { name: "Kuwait", coords: [29.3759, 47.9774], category: "assessment", connections: 0, avgSupportTime: 0, webinars: 0, avgWebinarTime: 0 , recordings: 0, avgRecordingTime: 0} // Kuwait, Kuwait
 
 
 
 
 
 
 
 
 
196
  ];
197
- var map = L.map('map').setView([50.0755, 14.4378], 3);
198
 
 
 
199
  L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
200
- attribution: 'Map data &copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
201
  }).addTo(map);
202
 
203
- var categoryColors = {
204
  "assessment": "#0057b8",
205
- "deployment": "#008000",
206
  "operative": "#008000",
207
  "paused": "#FFD700",
208
  "maintenance": "#FFA500",
209
  "removed": "#FF0000"
210
- };
211
 
212
  var markers = [];
213
- var chart1 = null;
214
- var chart2 = null;
215
- var chart3 = null;
216
- var chart4 = null;
217
- var chart5 = null;
218
- var chart6 = null;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
219
 
220
  function updateMarkers() {
221
  var selectedCategory = document.getElementById("category").value;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
222
 
 
 
 
 
223
  markers.forEach(marker => map.removeLayer(marker));
224
  markers = [];
225
 
226
- var filteredLocations = locations.filter(location => selectedCategory === "all" || location.category === selectedCategory);
227
-
228
  filteredLocations.forEach(location => {
 
 
229
  var marker = L.circleMarker(location.coords, {
230
- color: categoryColors[location.category],
231
- fillColor: categoryColors[location.category],
232
  fillOpacity: 0.8,
233
  radius: 8
234
  }).bindPopup(`<b>${location.name}</b><br>Categoría: ${location.category}<br>Conexiones: ${location.connections}<br>Soporte: ${location.avgSupportTime} hrs`);
235
-
236
  marker.addTo(map);
237
  markers.push(marker);
238
  });
239
 
 
240
  if (markers.length > 1) {
241
  var group = new L.featureGroup(markers);
242
- map.fitBounds(group.getBounds());
243
  } else if (markers.length === 1) {
244
  map.setView(markers[0].getLatLng(), 6);
245
  } else {
246
- map.setView([45, 10], 3);
247
  }
248
 
 
249
  updateCharts(filteredLocations);
250
  }
251
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
252
 
253
-
 
 
254
 
255
- function updateCharts(filteredLocations) {
256
- var cityNames = filteredLocations.map(location => location.name);
257
-
258
- var connectionsData = filteredLocations.map(location => location.connections);
259
- var avgSupportTimeData = filteredLocations.map(location => location.avgSupportTime);
260
- var webinarData = filteredLocations.map(location => location.webinars);
261
- var avgWebinarTimeData = filteredLocations.map(location => location.avgWebinarTime);
262
- var recordingData = filteredLocations.map(location => location.recordings);
263
- var avgRecordingTimeData = filteredLocations.map(location => location.avgRecordingTime);
264
-
265
- var categoryColorsData = filteredLocations.map(location => categoryColors[location.category]);
266
-
267
- if (chart1) chart1.destroy();
268
- if (chart2) chart2.destroy();
269
- if (chart3) chart3.destroy();
270
- if (chart4) chart4.destroy();
271
- if (chart5) chart3.destroy();
272
- if (chart6) chart4.destroy();
273
-
274
- var ctx1 = document.getElementById('connectionsChart').getContext('2d');
275
- chart1 = new Chart(ctx1, {
276
- type: 'bar',
277
- data: {
278
- labels: cityNames,
279
- datasets: [{
280
- label: 'Monthly Remote Connections',
281
- data: connectionsData,
282
- backgroundColor: categoryColorsData,
283
- borderColor: '#333',
284
- borderWidth: 1,
285
- borderRadius: 5,
286
- hoverBackgroundColor: 'rgba(0, 0, 0, 0.2)'
287
- }]
288
- },
289
- options: {
290
- plugins: {
291
- legend: { display: false },
292
- title: {
293
- display: true,
294
- text: 'Monthly Remote connections',
295
- font: {
296
- size: 12
297
- },
298
- color: '#333'
299
- }
300
- },
301
- scales: {
302
- x: {
303
- ticks: { color: '#333' },
304
- grid: { display: false }
305
- },
306
- y: {
307
- ticks: { color: '#333' },
308
- grid: { color: 'rgba(0,0,0,0.1)' }
309
- }
310
- },
311
- animation: {
312
- duration: 1000,
313
- easing: 'easeInOutBounce'
314
- }
315
 
316
- }
317
- });
318
 
319
- var ctx2 = document.getElementById('supportTimeChart').getContext('2d');
320
- chart2 = new Chart(ctx2, {
321
- type: 'bar',
322
- data: {
323
- labels: cityNames,
324
- datasets: [{
325
- label: 'Average Monthly Support Time (hrs)',
326
- data: avgSupportTimeData,
327
- backgroundColor: categoryColorsData,
328
- borderColor: '#333',
329
- borderWidth: 1,
330
- borderRadius: 5,
331
- hoverBackgroundColor: 'rgba(0, 0, 0, 0.2)'
332
- }]
333
- },
334
- options: {
335
- plugins: {
336
- legend: { display: false },
337
- title: {
338
- display: true,
339
- text: 'Average Monthly Support Time (hrs)',
340
- font: {
341
- size: 12
342
- },
343
- color: '#333'
344
- }
345
- },
346
- scales: {
347
- x: {
348
- ticks: { color: '#333' },
349
- grid: { display: false }
350
- },
351
- y: {
352
- ticks: { color: '#333' },
353
- grid: { color: 'rgba(0,0,0,0.1)' }
354
- }
355
- },
356
- animation: {
357
- duration: 1000,
358
- easing: 'easeInOutBounce'
359
- }
360
- }
361
- });
362
-
363
- var ctx3 = document.getElementById('webinarChart').getContext('2d');
364
- chart3 = new Chart(ctx3, {
365
- type: 'bar',
366
- data: {
367
- labels: cityNames,
368
- datasets: [{
369
- label: 'Average Monthly Webinar Connections',
370
- data: webinarData,
371
- backgroundColor: categoryColorsData,
372
- borderColor: '#333',
373
- borderWidth: 1,
374
- borderRadius: 5,
375
- hoverBackgroundColor: 'rgba(0, 0, 0, 0.2)'
376
- }]
377
- },
378
- options: {
379
- plugins: {
380
- legend: { display: false },
381
- title: {
382
- display: true,
383
- text: 'Monthly Webinar Connections',
384
- font: {
385
- size: 12
386
- },
387
- color: '#333'
388
- }
389
- },
390
- scales: {
391
- x: {
392
- ticks: { color: '#333' },
393
- grid: { display: false }
394
- },
395
- y: {
396
- ticks: { color: '#333' },
397
- grid: { color: 'rgba(0,0,0,0.1)' }
398
- }
399
- },
400
- animation: {
401
- duration: 1000,
402
- easing: 'easeInOutBounce'
403
- }
404
- }
405
- });
406
 
407
- var ctx4 = document.getElementById('webinarTime').getContext('2d');
408
- chart4 = new Chart(ctx4, {
409
- type: 'bar',
410
- data: {
411
- labels: cityNames,
412
- datasets: [{
413
- label: 'Average Monthly Webinar Time (hrs/session)',
414
- data: avgWebinarTimeData,
415
- backgroundColor: categoryColorsData,
416
- borderColor: '#333',
417
- borderWidth: 1,
418
- borderRadius: 5,
419
- hoverBackgroundColor: 'rgba(0, 0, 0, 0.2)'
420
- }]
421
- },
422
- options: {
423
- plugins: {
424
- legend: { display: false },
425
- title: {
426
- display: true,
427
- text: 'Average Monthly Webinar Time (hrs/session)',
428
- font: {
429
- size: 12
430
- },
431
- color: '#333'
432
- }
433
- },
434
- scales: {
435
- x: {
436
- ticks: { color: '#333' },
437
- grid: { display: false }
438
- },
439
- y: {
440
- ticks: { color: '#333' },
441
- grid: { color: 'rgba(0,0,0,0.1)' }
442
- }
443
- },
444
- animation: {
445
- duration: 1000,
446
- easing: 'easeInOutBounce'
447
- }
448
- }
449
- });
450
 
 
 
 
451
 
452
 
453
- /*RECORDINGS*/
454
-
455
- var ctx5 = document.getElementById('recordingChart').getContext('2d');
456
- chart5 = new Chart(ctx5, {
457
- type: 'bar',
458
- data: {
459
- labels: cityNames,
460
- datasets: [{
461
- label: 'Average Monthly Recordings',
462
- data: recordingData,
463
- backgroundColor: categoryColorsData,
464
- borderColor: '#333',
465
- borderWidth: 1,
466
- borderRadius: 5,
467
- hoverBackgroundColor: 'rgba(0, 0, 0, 0.2)'
468
- }]
469
- },
470
- options: {
471
- plugins: {
472
- legend: { display: false },
473
- title: {
474
- display: true,
475
- text: 'Monthly Recordings',
476
- font: {
477
- size: 12
478
- },
479
- color: '#333'
480
- }
481
- },
482
- scales: {
483
- x: {
484
- ticks: { color: '#333' },
485
- grid: { display: false }
486
- },
487
- y: {
488
- ticks: { color: '#333' },
489
- grid: { color: 'rgba(0,0,0,0.1)' }
490
- }
491
- },
492
- animation: {
493
- duration: 1000,
494
- easing: 'easeInOutBounce'
495
- }
496
- }
497
- });
498
 
499
- var ctx6 = document.getElementById('recordingTime').getContext('2d');
500
- chart6 = new Chart(ctx6, {
501
- type: 'bar',
502
- data: {
503
- labels: cityNames,
504
- datasets: [{
505
- label: 'Average Monthly Recording Time (hrs)',
506
- data: avgRecordingTimeData,
507
- backgroundColor: categoryColorsData,
508
- borderColor: '#333',
509
- borderWidth: 1,
510
- borderRadius: 5,
511
- hoverBackgroundColor: 'rgba(0, 0, 0, 0.2)'
512
- }]
513
- },
514
- options: {
515
- plugins: {
516
- legend: { display: false },
517
- title: {
518
- display: true,
519
- text: 'Average Monthly Recording Time (hrs)',
520
- font: {
521
- size: 12
522
- },
523
- color: '#333'
524
- }
525
- },
526
- scales: {
527
- x: {
528
- ticks: { color: '#333' },
529
- grid: { display: false }
530
- },
531
- y: {
532
- ticks: { color: '#333' },
533
- grid: { color: 'rgba(0,0,0,0.1)' }
534
- }
535
- },
536
- animation: {
537
- duration: 1000,
538
- easing: 'easeInOutBounce'
539
- }
540
- }
541
  });
542
 
543
- }
544
-
545
- updateMarkers();
546
  </script>
547
 
548
  </body>
 
8
  <script src="https://unpkg.com/leaflet/dist/leaflet.js"></script>
9
  <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
10
  <style>
11
+ /* [MISMO CSS QUE ANTES - OMITIDO POR BREVEDAD] */
12
+ /* Paleta de colores corporativa */
13
+ :root {
14
  --primary-blue: #0057b8;
15
  --secondary-blue: #003f7f;
16
  --light-gray: #f4f4f4;
 
36
  }
37
 
38
 
39
+ /*CAMBIO COLUMNAS*/
40
 
41
  /* Contenedor principal con grid */
42
  .dashboard {
 
49
 
50
  /* Controles */
51
  #controls {
52
+ grid-column: 1 / -1; /* Hacer que los controles ocupen todo el ancho */
53
  display: flex;
54
  justify-content: center;
55
  align-items: center;
56
  background: white;
57
+ padding: 15px; /* Aumentado padding */
58
  border-radius: 10px;
59
  box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
60
+ margin-bottom: 20px; /* Añadido margen inferior */
61
+ flex-wrap: wrap; /* Permitir que los controles pasen a la siguiente línea si no caben */
62
  }
63
 
64
+ #controls label {
65
+ margin: 0 5px 0 15px; /* Ajuste de margen para etiquetas */
66
+ }
67
+
68
+ #controls select {
69
  padding: 8px;
70
  font-size: 16px;
71
  border: 2px solid var(--primary-blue);
 
73
  background: white;
74
  color: var(--dark-gray);
75
  cursor: pointer;
76
+ margin: 5px; /* Añadido margen alrededor de los selectores */
77
  }
78
 
79
  /* Estilos del mapa */
 
85
  }
86
 
87
  /* Contenedor de gráficos */
88
+ .chart-container { /* Este contenedor ya no se usa directamente para los gráficos principales */
89
+ /* display: grid;
90
  grid-template-columns: repeat(2, 1fr);
91
+ gap: 20px; */
92
  }
93
 
94
  .chart-box {
 
96
  padding: 20px;
97
  border-radius: 10px;
98
  box-shadow: 0px 4px 8px rgba(0, 0, 0, 0.1);
99
+ /* Los canvas dentro del chart-box se manejarán individualmente o por defecto */
 
 
 
 
100
  }
101
 
102
  canvas {
103
  width: 100%;
104
+ /* Altura ajustada para mejor visualización cuando hay varios */
105
+ height: auto; /* Permitir que el contenedor defina la altura o usar ratio */
106
+ max-height: 250px; /* Limitar altura máxima si es necesario */
107
+ margin-bottom: 15px; /* Espacio entre gráficos dentro del mismo chart-box */
108
  }
109
+ .chart-box canvas:last-child {
110
+ margin-bottom: 0; /* Quitar margen inferior al último canvas */
111
+ }
112
+
113
 
114
  /* Responsive */
115
+ @media (max-width: 1200px) { /* Ajuste para pantallas medianas/grandes */
116
+ .dashboard {
117
+ grid-template-columns: 1fr 1fr; /* 2 columnas */
118
+ grid-template-rows: auto auto auto;
119
+ }
120
+ #map {
121
+ grid-column: 1 / -1; /* Mapa ocupa todo el ancho */
122
+ grid-row: 2; /* Mapa en la segunda fila */
123
+ }
124
+ .chart-box:nth-of-type(1) { /* Primer contenedor de gráficos */
125
+ grid-column: 1 / 2;
126
+ grid-row: 3;
127
+ }
128
+ .chart-box:nth-of-type(2) { /* Segundo contenedor de gráficos */
129
+ grid-column: 2 / 3;
130
+ grid-row: 3;
131
+ }
132
+ #controls {
133
+ grid-row: 1; /* Controles en la primera fila */
134
+ }
135
+ }
136
+
137
  @media (max-width: 768px) {
138
+ .dashboard {
139
+ grid-template-columns: 1fr; /* 1 columna */
140
+ grid-template-rows: auto auto auto auto; /* Una fila para cada elemento principal */
141
  }
142
+ #map, .chart-box, #controls {
143
+ grid-column: 1 / -1; /* Todos ocupan el ancho completo */
144
+ }
145
+ #controls { grid-row: 1; }
146
+ #map { grid-row: 2; }
147
+ .chart-box:nth-of-type(1) { grid-row: 3; } /* Primer chart-box */
148
+ .chart-box:nth-of-type(2) { grid-row: 4; } /* Segundo chart-box */
149
+
150
+ #controls {
151
+ flex-direction: column; /* Apilar controles verticalmente */
152
+ align-items: stretch; /* Estirar elementos */
153
+ }
154
+ #controls label {
155
+ margin: 10px 0 5px 0; /* Ajustar margen para layout vertical */
156
+ }
157
+ #controls select {
158
+ width: 100%; /* Ocupar todo el ancho disponible */
159
+ box-sizing: border-box; /* Incluir padding/border en el ancho */
160
+ }
161
  }
162
  </style>
163
  </head>
164
  <body>
165
 
166
  <h1>Matrix - Dashboard</h1>
167
+
168
+ <!-- Controles -->
169
  <div id="controls">
170
  <label for="category">Status:</label>
171
  <select id="category" onchange="updateMarkers()">
 
177
  <option value="maintenance">Maintenance</option>
178
  <option value="removed">Removed</option>
179
  </select>
 
 
 
180
 
181
+   
 
 
 
 
 
 
 
 
 
182
 
183
+ <label for="monthFrom">From:</label>
184
+ <select id="monthFrom" onchange="updateMarkers()">
185
+ <option value="1">Jan</option>
186
+ <option value="2">Feb</option>
187
+ <option value="3">Mar</option>
188
+ <!-- Podrías añadir más meses si creas más variables de datos -->
189
+ <option value="4">Apr (no data)</option>
190
+ </select>
191
+
192
+   
193
 
194
+ <label for="monthTo">To:</label>
195
+ <select id="monthTo" onchange="updateMarkers()">
196
+ <option value="1">Jan</option>
197
+ <option value="2">Feb</option>
198
+ <option value="3" selected>Mar</option> <!-- Seleccionado por defecto -->
199
+ <!-- Podrías añadir más meses si creas más variables de datos -->
200
+ <option value="4">Apr (no data)</option>
201
+ </select>
202
+ </div>
203
 
204
+ <!-- Dashboard Grid -->
205
+ <div class="dashboard">
206
  <div class="chart-box">
207
  <canvas id="connectionsChart"></canvas>
208
  <canvas id="webinarChart"></canvas>
209
  <canvas id="recordingChart"></canvas>
210
  </div>
 
 
211
  <div id="map"></div>
 
212
  <div class="chart-box">
213
  <canvas id="supportTimeChart"></canvas>
214
  <canvas id="webinarTime"></canvas>
215
  <canvas id="recordingTime"></canvas>
216
  </div>
 
 
 
 
 
 
 
 
 
217
  </div>
 
218
 
219
+
220
  <script>
221
+ // --- DATOS MENSUALES DE EJEMPLO ---
222
+ const locationsJan = [
223
+ { name: "Ragusa", coords: [36.9257, 14.7244], category: "operative", connections: 11, avgSupportTime: 12, webinars: 1, avgWebinarTime: 1 , recordings: 0, avgRecordingTime: 0},
224
+ { name: "Seville", coords: [37.3886, -5.9823], category: "operative", connections: 7, avgSupportTime: 2, webinars: 1, avgWebinarTime: 1 , recordings: 0, avgRecordingTime: 0},
225
+ { name: "Groningen", coords: [53.2194, 6.5665], category: "operative", connections: 1, avgSupportTime: 3, webinars: 0, avgWebinarTime: 0 , recordings: 0, avgRecordingTime: 0},
226
+ { name: "Cape Town", coords: [-33.9249, 18.4241], category: "operative", connections: 0, avgSupportTime: 0.5, webinars: 13, avgWebinarTime: 1 , recordings: 0, avgRecordingTime: 0},
227
+ { name: "Bern", coords: [46.9481, 7.4474], category: "assessment", connections: 0, avgSupportTime: 0, webinars: 0, avgWebinarTime: 0 , recordings: 0, avgRecordingTime: 0},
228
+ { name: "Kiel", coords: [54.3233, 10.1228], category: "removed", connections: 0, avgSupportTime: 0, webinars: 0, avgWebinarTime: 0 , recordings: 0, avgRecordingTime: 0},
229
+ { name: "Le Mans", coords: [48.0077, 0.1996], category: "assessment", connections: 0, avgSupportTime: 0, webinars: 0, avgWebinarTime: 0 , recordings: 0, avgRecordingTime: 0},
230
+ { name: "Gdansk", coords: [54.3520, 18.6466], category: "assessment", connections: 0, avgSupportTime: 0, webinars: 0, avgWebinarTime: 0 , recordings: 0, avgRecordingTime: 0},
231
+ { name: "Prague", coords: [50.0755, 14.4378], category: "assessment", connections: 0, avgSupportTime: 0, webinars: 0, avgWebinarTime: 0 , recordings: 0, avgRecordingTime: 0},
232
+ { name: "Kuwait", coords: [29.3759, 47.9774], category: "deployment", connections: 0, avgSupportTime: 0, webinars: 0, avgWebinarTime: 0 , recordings: 0, avgRecordingTime: 0} // Cambiado a deployment
233
+ ];
234
+ const locationsFeb = [
235
+ { name: "Ragusa", coords: [36.9257, 14.7244], category: "operative", connections: 21, avgSupportTime: 12, webinars: 3, avgWebinarTime: 1 , recordings: 0, avgRecordingTime: 0},
236
+ { name: "Seville", coords: [37.3886, -5.9823], category: "operative", connections: 5, avgSupportTime: 2, webinars: 6, avgWebinarTime: 1 , recordings: 0, avgRecordingTime: 0},
237
+ { name: "Groningen", coords: [53.2194, 6.5665], category: "operative", connections: 13, avgSupportTime: 3, webinars: 0, avgWebinarTime: 0 , recordings: 0, avgRecordingTime: 0},
238
+ { name: "Cape Town", coords: [-33.9249, 18.4241], category: "operative", connections: 13, avgSupportTime: 0.5, webinars: 0, avgWebinarTime: 1 , recordings: 0, avgRecordingTime: 0},
239
+ { name: "Bern", coords: [46.9481, 7.4474], category: "assessment", connections: 0, avgSupportTime: 0, webinars: 0, avgWebinarTime: 0 , recordings: 0, avgRecordingTime: 0}, // Cambiado a maintenance
240
+ { name: "Kiel", coords: [54.3233, 10.1228], category: "removed", connections: 0, avgSupportTime: 0, webinars: 0, avgWebinarTime: 0 , recordings: 0, avgRecordingTime: 0},
241
+ { name: "Le Mans", coords: [48.0077, 0.1996], category: "assessment", connections: 0, avgSupportTime: 0, webinars: 0, avgWebinarTime: 0 , recordings: 0, avgRecordingTime: 0},
242
+ { name: "Gdansk", coords: [54.3520, 18.6466], category: "assessment", connections: 0, avgSupportTime: 0, webinars: 0, avgWebinarTime: 0 , recordings: 0, avgRecordingTime: 0}, // Cambiado a deployment
243
+ { name: "Prague", coords: [50.0755, 14.4378], category: "assessment", connections: 0, avgSupportTime: 0, webinars: 0, avgWebinarTime: 0 , recordings: 0, avgRecordingTime: 0},
244
+ { name: "Kuwait", coords: [29.3759, 47.9774], category: "assessment", connections: 0, avgSupportTime: 0, webinars: 0, avgWebinarTime: 0 , recordings: 0, avgRecordingTime: 0}
245
+ ];
246
+ const locationsMar = [
247
+ { 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
248
+ { name: "Seville", coords: [37.3886, -5.9823], category: "operative", connections: 5, avgSupportTime: 2, webinars: 0, avgWebinarTime: 1 , recordings: 0, avgRecordingTime: 0},
249
+ { name: "Groningen", coords: [53.2194, 6.5665], category: "operative", connections: 3, avgSupportTime: 3, webinars: 0, avgWebinarTime: 0 , recordings: 0, avgRecordingTime: 0},
250
+ { 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
251
+ { name: "Bern", coords: [46.9481, 7.4474], category: "operative", connections: 3, avgSupportTime: 0, webinars: 0, avgWebinarTime: 0 , recordings: 0, avgRecordingTime: 0}, // Vuelve a operative
252
+ { name: "Kiel", coords: [54.3233, 10.1228], category: "removed", connections: 0, avgSupportTime: 0, webinars: 0, avgWebinarTime: 0 , recordings: 0, avgRecordingTime: 0},
253
+ { name: "Le Mans", coords: [48.0077, 0.1996], category: "assessment", connections: 0, avgSupportTime: 0, webinars: 0, avgWebinarTime: 0 , recordings: 0, avgRecordingTime: 0},
254
+ { name: "Gdansk", coords: [54.3520, 18.6466], category: "assessment", connections: 0, avgSupportTime: 0, webinars: 0, avgWebinarTime: 0 , recordings: 0, avgRecordingTime: 0},
255
+ { name: "Prague", coords: [50.0755, 14.4378], category: "assessment", connections: 0, avgSupportTime: 0, webinars: 0, avgWebinarTime: 0 , recordings: 0, avgRecordingTime: 0},
256
+ { 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
257
  ];
 
258
 
259
+ // Mapa y colores (sin cambios)
260
+ var map = L.map('map').setView([50.0755, 14.4378], 3);
261
  L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
262
+ attribution: 'Map data © <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
263
  }).addTo(map);
264
 
265
+ var categoryColors = { /* ... Mismos colores ... */
266
  "assessment": "#0057b8",
267
+ "deployment": "#ff8c00", // Naranja para deployment
268
  "operative": "#008000",
269
  "paused": "#FFD700",
270
  "maintenance": "#FFA500",
271
  "removed": "#FF0000"
272
+ };
273
 
274
  var markers = [];
275
+ var chart1 = null, chart2 = null, chart3 = null, chart4 = null, chart5 = null, chart6 = null;
276
+
277
+
278
+ /**
279
+ * Agrega datos de múltiples datasets mensuales.
280
+ * @param {Array<Array<Object>>} datasets - Array de datasets (ej: [locationsJan, locationsFeb]).
281
+ * @returns {Array<Object>} - Un único dataset con valores agregados.
282
+ */
283
+ function aggregateMonthlyData(datasets) {
284
+ const aggregated = {}; // Usar objeto para agrupar por nombre
285
+
286
+ datasets.forEach(monthlyData => {
287
+ monthlyData.forEach(location => {
288
+ const name = location.name;
289
+ if (!aggregated[name]) {
290
+ // Si es la primera vez que vemos esta ubicación, inicializarla
291
+ aggregated[name] = {
292
+ ...location, // Copia coords, category (la del primer mes encontrado)
293
+ totalConnections: 0,
294
+ totalSupportTime: 0,
295
+ totalWebinars: 0,
296
+ totalWebinarTime: 0,
297
+ totalRecordings: 0,
298
+ totalRecordingTime: 0,
299
+ };
300
+ // Usar la categoría del *último* mes en el rango como la representativa
301
+ if (datasets.length > 0) {
302
+ const lastMonthData = datasets[datasets.length - 1];
303
+ const lastLocationState = lastMonthData.find(loc => loc.name === name);
304
+ if (lastLocationState) {
305
+ aggregated[name].category = lastLocationState.category;
306
+ }
307
+ }
308
+
309
+ }
310
+
311
+ // Acumular totales
312
+ aggregated[name].totalConnections += location.connections;
313
+ aggregated[name].totalSupportTime += location.connections * location.avgSupportTime; // Tiempo total = conexiones * promedio
314
+ aggregated[name].totalWebinars += location.webinars;
315
+ aggregated[name].totalWebinarTime += location.webinars * location.avgWebinarTime;
316
+ aggregated[name].totalRecordings += location.recordings;
317
+ aggregated[name].totalRecordingTime += location.recordings * location.avgRecordingTime;
318
+
319
+ });
320
+ });
321
+
322
+ // Calcular promedios finales y formatear salida
323
+ const finalData = Object.values(aggregated).map(loc => {
324
+ // Evitar división por cero
325
+ const avgSupportTime = loc.totalConnections > 0 ? (loc.totalSupportTime / loc.totalConnections) : 0;
326
+ const avgWebinarTime = loc.totalWebinars > 0 ? (loc.totalWebinarTime / loc.totalWebinars) : 0;
327
+ const avgRecordingTime = loc.totalRecordings > 0 ? (loc.totalRecordingTime / loc.totalRecordings) : 0;
328
+
329
+ return {
330
+ ...loc, // Mantiene name, coords, category
331
+ connections: loc.totalConnections,
332
+ avgSupportTime: parseFloat(avgSupportTime.toFixed(2)), // Redondear a 2 decimales
333
+ webinars: loc.totalWebinars,
334
+ avgWebinarTime: parseFloat(avgWebinarTime.toFixed(2)),
335
+ recordings: loc.totalRecordings,
336
+ avgRecordingTime: parseFloat(avgRecordingTime.toFixed(2)),
337
+ // Eliminar propiedades temporales si se desea
338
+ // totalConnections, totalSupportTime, etc. ya no son necesarias
339
+ };
340
+ });
341
+
342
+ return finalData;
343
+ }
344
+
345
 
346
  function updateMarkers() {
347
  var selectedCategory = document.getElementById("category").value;
348
+ var selectedMonthFrom = parseInt(document.getElementById("monthFrom").value, 10);
349
+ var selectedMonthTo = parseInt(document.getElementById("monthTo").value, 10);
350
+
351
+ let datasetsToCombine = [];
352
+
353
+ // Validar rango básico
354
+ if (selectedMonthFrom > selectedMonthTo) {
355
+ console.warn("El mes 'From' no puede ser posterior al mes 'To'. Mostrando datos solo para 'From'.");
356
+ selectedMonthTo = selectedMonthFrom; // O mostrar un error/nada
357
+ }
358
+
359
+
360
+ // Determinar qué datasets incluir basándose en el rango (solo para meses 1, 2, 3)
361
+ if (selectedMonthFrom <= 1 && selectedMonthTo >= 1) datasetsToCombine.push(locationsJan);
362
+ if (selectedMonthFrom <= 2 && selectedMonthTo >= 2) datasetsToCombine.push(locationsFeb);
363
+ if (selectedMonthFrom <= 3 && selectedMonthTo >= 3) datasetsToCombine.push(locationsMar);
364
+ // Añadir más 'if' si se crean locationsApr, locationsMay, etc.
365
+
366
+ let dataToDisplay = [];
367
+ if (datasetsToCombine.length > 0) {
368
+ // Si hay más de un mes, agregar. Si solo hay uno, aggregateMonthlyData lo manejará.
369
+ dataToDisplay = aggregateMonthlyData(datasetsToCombine);
370
+ } else {
371
+ console.log("No hay datos disponibles para el rango de meses seleccionado (1-3).");
372
+ }
373
+
374
+ // --- Filtrado por Categoría (se aplica DESPUÉS de agregar por mes) ---
375
+ let filteredLocations = dataToDisplay.filter(location =>
376
+ selectedCategory === "all" || location.category === selectedCategory
377
+ );
378
 
379
+ console.log(`Mostrando datos agregados de Mes ${selectedMonthFrom} a ${selectedMonthTo}. Categoría: ${selectedCategory}. Ubicaciones filtradas: ${filteredLocations.length}`);
380
+
381
+
382
+ // --- Actualización de Mapa y Gráficos (sin cambios internos, solo usan filteredLocations) ---
383
  markers.forEach(marker => map.removeLayer(marker));
384
  markers = [];
385
 
 
 
386
  filteredLocations.forEach(location => {
387
+ // Usar la categoría (posiblemente actualizada por la agregación)
388
+ const color = categoryColors[location.category] || '#CCCCCC';
389
  var marker = L.circleMarker(location.coords, {
390
+ color: color,
391
+ fillColor: color,
392
  fillOpacity: 0.8,
393
  radius: 8
394
  }).bindPopup(`<b>${location.name}</b><br>Categoría: ${location.category}<br>Conexiones: ${location.connections}<br>Soporte: ${location.avgSupportTime} hrs`);
395
+
396
  marker.addTo(map);
397
  markers.push(marker);
398
  });
399
 
400
+ // Ajustar vista del mapa
401
  if (markers.length > 1) {
402
  var group = new L.featureGroup(markers);
403
+ try { map.fitBounds(group.getBounds().pad(0.3)); } catch (e) { /*...*/ }
404
  } else if (markers.length === 1) {
405
  map.setView(markers[0].getLatLng(), 6);
406
  } else {
407
+ map.setView([48, 15], 4);
408
  }
409
 
410
+ // Actualizar gráficos
411
  updateCharts(filteredLocations);
412
  }
413
 
414
+ // --- La función updateCharts permanece sin cambios ---
415
+ function updateCharts(filteredLocations) {
416
+ // ... (código idéntico al de la versión anterior para crear/actualizar los 6 gráficos) ...
417
+ var cityNames = filteredLocations.map(location => location.name);
418
+
419
+ var connectionsData = filteredLocations.map(location => location.connections);
420
+ var avgSupportTimeData = filteredLocations.map(location => location.avgSupportTime);
421
+ var webinarData = filteredLocations.map(location => location.webinars);
422
+ var avgWebinarTimeData = filteredLocations.map(location => location.avgWebinarTime);
423
+ var recordingData = filteredLocations.map(location => location.recordings);
424
+ var avgRecordingTimeData = filteredLocations.map(location => location.avgRecordingTime);
425
+
426
+ // Usar el color de la categoría final (después de la agregación)
427
+ var categoryColorsData = filteredLocations.map(location => categoryColors[location.category] || '#CCCCCC');
428
+
429
+ // Destruir gráficos existentes
430
+ if (chart1) chart1.destroy();
431
+ if (chart2) chart2.destroy();
432
+ if (chart3) chart3.destroy();
433
+ if (chart4) chart4.destroy();
434
+ if (chart5) chart5.destroy();
435
+ if (chart6) chart6.destroy();
436
+
437
+ // --- Opciones comunes para gráficos (reutilizadas de la versión anterior) ---
438
+ var commonOptions = (titleText) => ({
439
+ responsive: true,
440
+ maintainAspectRatio: false,
441
+ plugins: { legend: { display: false }, title: { display: true, text: titleText, font: { size: 14 }, color: '#333', padding: { top: 5, bottom: 15 } } },
442
+ 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)' } } },
443
+ animation: { duration: 800, easing: 'easeOutQuad' }
444
+ });
445
+ var commonDatasetOptions = (data, colors) => ({
446
+ data: data, backgroundColor: colors, borderColor: 'rgba(51, 51, 51, 0.5)', borderWidth: 1, borderRadius: 5, hoverBackgroundColor: 'rgba(0, 0, 0, 0.2)'
447
+ });
448
 
449
+ // --- Creación de Gráficos ---
450
+ var ctx1 = document.getElementById('connectionsChart').getContext('2d');
451
+ chart1 = new Chart(ctx1, { type: 'bar', data: { labels: cityNames, datasets: [commonDatasetOptions(connectionsData, categoryColorsData)] }, options: commonOptions('Total Remote connections (Selected Period)') });
452
 
453
+ var ctx2 = document.getElementById('supportTimeChart').getContext('2d');
454
+ 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
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
455
 
456
+ var ctx3 = document.getElementById('webinarChart').getContext('2d');
457
+ chart3 = new Chart(ctx3, { type: 'bar', data: { labels: cityNames, datasets: [commonDatasetOptions(webinarData, categoryColorsData)] }, options: commonOptions('Total Webinar Connections (Selected Period)') });
458
 
459
+ var ctx4 = document.getElementById('webinarTime').getContext('2d');
460
+ 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
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
461
 
462
+ var ctx5 = document.getElementById('recordingChart').getContext('2d');
463
+ chart5 = new Chart(ctx5, { type: 'bar', data: { labels: cityNames, datasets: [commonDatasetOptions(recordingData, categoryColorsData)] }, options: commonOptions('Total Recordings (Selected Period)') });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
464
 
465
+ var ctx6 = document.getElementById('recordingTime').getContext('2d');
466
+ 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
467
+ }
468
 
469
 
470
+ // Llamada inicial
471
+ document.addEventListener('DOMContentLoaded', function() {
472
+ // Establecer mes 'To' por defecto (ej: Marzo = 3)
473
+ const monthToSelect = document.getElementById('monthTo');
474
+ monthToSelect.value = 3;
475
+ // Establecer mes 'From' por defecto (ej: Enero = 1)
476
+ const monthFromSelect = document.getElementById('monthFrom');
477
+ monthFromSelect.value = 1;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
478
 
479
+ updateMarkers(); // Carga inicial con el rango por defecto (Jan-Mar)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
480
  });
481
 
 
 
 
482
  </script>
483
 
484
  </body>