{% extends 'base.html.twig' %}
{% if form is defined %}
{% form_theme form 'bootstrap_4_layout.html.twig' %}
{% endif %}
{% block stylesheets %}
<link href="https://unpkg.com/gijgo@1.9.14/css/gijgo.min.css" rel="stylesheet" type="text/css" />
<link rel="stylesheet" href="{{ asset('plugins/toastr/toastr.min.css') }}">
<style>
label {
margin: 0 5px;
}
.form-group {
margin-right: 10px;
}
.barra-contenedor {
display: flex;
align-items: flex-end; /* Para que la barra crezca desde abajo */
height: 300px; /* Misma altura que el pie chart */
border: 1px solid #4d4d4d;
width: 70px; /* Ancho de la barra */
margin: 10px auto;
}
.barra-progreso {
width: 100%;
background-color: red; /* Color rojo */
transition: height 0.5s; /* Efecto suave */
}
.titulo-vertical {
/* Estilos para girar el texto a la izquierda */
transform: rotate(-90deg);
transform-origin: left top;
position: absolute;
margin-top: 150px; /* Ajuste manual para centrar */
left: 110px;
width: 300px;
text-align: center;
}
</style>
{% endblock %}
{% block body %}
<div class="content-wrapper">
<!-- Content Header (Page header) -->
<section class="content-header">
<div class="card">
<div class="card-header">
<h1 class="card-title">Indicadores</h1>
<div class="card-tools">
<ol class="breadcrumb float-sm-right">
<li class="breadcrumb-item">Inicio</li>
<li class="breadcrumb-item active">Indicadores</li>
</ol>
</div>
</div>
</div>
</section>
<!-- Main content -->
<section class="content">
<!-- Default box -->
<div class="card">
<div class="card-header">
<div class="row">
{% if form is defined %}
{{ form_start(form, {'attr': {'class': 'form-inline col-md-auto', 'id': 'form_dashboard_filtro'}}) }}
{{ form_row(form.date_inicio) }}
{{ form_row(form.date_fin) }}
{% if form.unidad is defined %}
<div class="ml-3">{{ form_row(form.unidad) }}</div>
{% endif %}
{{ form_end(form) }}
{% endif %}
<div class="col text-right">
<button id="consultar" type="button" class="btn btn-outline-success mr-2" onclick="consultar()">
<i class="fas fa-search mr-2"></i> Consultar
</button>
</div>
</div>
</div>
</div>
<!-- /.card -->
<div class="row">
<div class="col">
<!-- Default box -->
<div class="card">
<div class="card-header">
<h3 class="card-title"><b>Índice de Equipo de Cómputo Actualizado (ECA).</b></h3>
</div>
<div class="card-body table-responsive p-0">
<div id="equipo"></div>
</div>
<!-- /.card-body -->
<div class="card-footer pb-0 pt-0">
<div class="row">
<div class="col-sm-9">
<span style="font-size: 0.8rem">
<b>ECA</b>: Índice de Equipo de Cómputo Actualizado<br>
<b>TE</b>: Total de equipos en el inventario (no considera equipo en estatus de baja).<br>
<b>EMHV</b>: Total de equipos en el inventario que cumplen con el Estándar Mínimo de Hardware Vigente.<br>
<b>Fórmula ECA</b>: <em> EMHV / TE *100.</em>
</span>
</div>
<div class="col-sm-3" style="font-size: 0.8rem">
<p>Fecha inicial: <span class="date-inicial"></span></p>
<p>Fecha final: <span class="date-final"></span></p>
<p>Fecha consulta: <span class="date-actual"></span></p>
</div>
</div>
</div>
<!-- /.card-footer-->
</div>
<!-- /.card -->
</div>
<div class="col">
<!-- Default box -->
<div class="card">
<div class="card-header">
<h3 class="card-title"><b>Eficacia del Mantenimiento Preventivo (EMP).</b></h3>
</div>
<div class="card-body table-responsive p-0">
<div class="row">
<div class="col-sm-3 col-md-2" style="padding-top: 15px;">
<div id="barra-pamp-col" class="barra-contenedor" style="display: none;">
<div id = 'titu' class="titulo-vertical"></div>
<div id="barraPAMP" class="barra-progreso"></div>
</div>
</div>
<div class="col-sm-9 col-md-10">
<div id="solicitud"></div>
</div>
</div>
</div>
<!-- /.card-body -->
<div class="card-footer pb-0 pt-0">
<div class="row">
<div class="col-sm-9">
<span style="font-size: 0.8rem;">
<b>EMP</b>: Eficacia de Mantenimiento Preventivo.<br>
<b>ST-Ev</b>: Total de Soportes Técnicos evaluados.<br>
<b>MP-Ev</b>: Total de mantenimientos preventivos evaluados.<br>
<b>MC-Ev</b>: Total de mantenimientos correctivos evaluados.<br>
<b>Fórmula EMP</b>: <em> MP-Ev / (MP-Ev + MC-Ev) *100</em>
</span>
</div>
<div class="col-sm-3" style="font-size: 0.8rem">
<p>Fecha inicial: <span class="date-inicial"></span></p>
<p>Fecha final: <span class="date-final"></span></p>
<p>Fecha consulta: <span class="date-actual"></span></p>
</div>
</div>
</div>
<!-- /.card-footer-->
</div>
<!-- /.card -->
</div>
</div>
<div class="row">
<div class="col">
<!-- Default box -->
<div class="card">
<div class="card-header">
<h3 class="card-title"><b>Resultados de la encuesta de evaluación de los mantenimientos</b></h3>
</div>
<div class="card-body table-responsive p-0">
<div id="evaluacion"></div>
</div>
<!-- /.card-body -->
<div class="card-footer p-0">
<ol style="font-size: 0.8rem">
<li><b>Sol-Ev</b>: Total de solicitudes de mantenimiento evaluadas.</li>
<li><b>Reincidencias</b>: Total de respuestas que manifiestan una reincidencia en la falla.</li>
<li><b>Reincidencia en fallas</b>: Porcentaje en la residencia de fallas.<b> Fórmula:</b> <em>Reincidencias / Sol-Ev * 100</em></li>
<li><b>TR</b>: Sumatoria de valoración del tiempo de respuesta en las solicitudes evaluadas (1->Deficiente … 5->Muy bueno).</li>
<li><b>S. tiempos de respuesta</b>: Porcentaje de satisfacción en los tiempos de respuesta. <b> Fórmula:</b> <em>TR / Sol-Ev * 100 (1->Deficiente … 5->Muy bueno).</em></li>
<li><b>Satisfacción</b>: Nivel de satisfacción en la solución en las solicitudes evaluadas (1->Deficiente … 5->Muy bueno).</li>
<li><b>S. solución proporcionada</b>: Porcentaje de satisfacción en la solución proporcionada.<b> Fórmula :</b> <em>Satisfacción / Sol-Ev * 100 (1->Deficiente … 5->Muy bueno).</em></li>
<li><b>SG</b>: Percepción de la satisfacción general en la atención de las solicitudes evaluadas (1->Deficiente … 5->Muy bueno).</li>
<li><b>S. general</b>: Porcentaje de satisfacción general de los servicios. <b> Fórmula:</b> <em>SG / Sol-Ev * 100 (1->Deficiente … 5->Muy bueno).</em></li>
<!--<li><b>Reincidencia en fallas presentadas</b>: <em>Sumatoria de reincidencias * 100 / Total de servicios evaluados.</em></li>
<li><b>Porcentaje de satisfacción en los tiempos de respuesta</b>: <em>(Sumatoria de satisfacción en los tiempos de respuesta * 100) / (Total de servicios evaluados * Calificación máxima <<5>>).</em></li>
<li><b>Porcentaje de satisfacción en la solución proporcionada</b>: <em>(Sumatoria de satisfacción en la solución proporcionada * 100) / (Total de servicios evaluados * Calificación máxima <<5>>).</em></li>
<li><b>Porcentaje de satisfacción general de los servicios</b>: <em>(Sumatoria de satisfacción general de los servicios * 100) / (Total de servicios evaluados * Calificación máxima <<5>>).</em></li>-->
</ol>
</div>
<!-- /.card-footer-->
</div>
<!-- /.card -->
</div>
</div>
</section>
<!-- /.content -->
</div>
{% endblock %}
{% block javascripts %}
<script src="https://code.highcharts.com/highcharts.js"></script>
<script src="https://code.highcharts.com/highcharts-more.js"></script>
<script src="https://code.highcharts.com/modules/exporting.js"></script>
<script src="https://code.highcharts.com/modules/export-data.js"></script>
<script src="https://code.highcharts.com/modules/accessibility.js"></script>
<script src="https://unpkg.com/gijgo@1.9.14/js/gijgo.min.js" type="text/javascript"></script>
<script src="https://unpkg.com/gijgo@1.9.14/js/messages/messages.es-es.js" type="text/javascript"></script>
<script src="{{ asset('plugins/toastr/toastr.min.js') }}"></script>
<script>
// Menu lateral
$("#reporte").addClass("menu-open");
$("#reporte").find('.nav-link:first').addClass("active");
$("#indicador").addClass("active");
/*$(function() {
let firstDayYear = new Date().getFullYear() + '-01-01';
let today = new Date().getFullYear() + '-' + (new Date().getMonth() + 1) + '-' + new Date().getDate();
$('#dashboard_filtro_date_inicio').datepicker({
showOtherMonths: true,
uiLibrary: 'bootstrap4',
modal: true,
header: true,
footer: true,
iconsLibrary: 'fontawesome',
locale: 'es-es',
format: 'dd-mm-yyyy',
value: firstDayYear,
maxDate: function () {
return $('#dashboard_filtro_date_fin').val();
}
});
$('#dashboard_filtro_date_fin').datepicker({
showOtherMonths: true,
uiLibrary: 'bootstrap4',
modal: true,
header: true,
footer: true,
iconsLibrary: 'fontawesome',
locale: 'es-es',
value: today,
format: 'dd-mm-yyyy',
minDate: function () {
return $('#dashboard_filtro_date_inicio').val();
}
});
});*/
function consultar(mpIndice) {
//$("#table-pamp").find($("tbody")).html("<tr><td colspan='18' class='text-center'><i class='fas fa-spinner fa-pulse'></i></td></tr>");
let btn = $("#consultar");
let formData = new FormData(document.getElementById("form_dashboard_filtro"));
const fecha = new Date();
// Formatear la fecha al formato "d/m/Y H:i:s"
const fechaFormateada = fecha.toLocaleString('es-MX', {
day: '2-digit',
month: '2-digit',
year: 'numeric',
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
hour12: false
}).replace(',', ''); // Eliminar la coma que agrega toLocaleString()
$.ajax({
type: 'POST',
url: '{{ path('app_dashboard') }}',
data: formData, // Adjuntar los campos del formulario enviado.
dataType: "json",
cache: false,
contentType: false,
processData: false,
beforeSend: function () {
btn.html('<i class="fas fa-spinner fa-pulse mr-2"></i> Consultar').prop('disabled', true);
},
success: function (data) {
let sumatoria = [
{ inicial: '{{ form.date_inicio.vars.value|date("d/m/Y") }}', final: '{{ form.date_fin.vars.value|date("d/m/Y") }}', actual: fechaFormateada},
{ tequipo: 0, temhv: 0, teca: 0},
{ tmc: 0, tmp: 0, temp: 0}
];
let equipo = data.data.equipo;
let solicitud = data.data.solicitud;
let evaluacion = data.data.evaluacion;
let equipoHTML =
`<figure class='highcharts-figure'>
<div id='equipoGrafico'></div>
</figure>
<br>
<table class='table table-sm table-hover text-nowrap'>
<thead>
<tr class='text-lightblue'>
<th>Unidad administrativa</th>
<th>TE</th>
<th>EMHV</th>
<th>ECA</th>
</tr>
</thead>
<tbody>`;
$.each(equipo, function (key, eq) {
equipoHTML +=
`<tr>
<td>${eq.nombre}</td>
<td>${eq.total}</td>
<td>${eq.emhv}</td>
<td>${eq.indice}</td>
</tr>`;
sumatoria[1].tequipo += parseInt(eq.total);
sumatoria[1].temhv += parseInt(eq.emhv);
});
sumatoria[1].teca = (sumatoria[1].temhv / sumatoria[1].tequipo) * 100;
// Agrega una fila al final con las sumas
equipoHTML +=
`<tr>
<td class="text-right"><strong>Totales estatales:    </strong></td>
<td><strong>${sumatoria[1].tequipo}</strong></td>
<td><strong>${sumatoria[1].temhv}</strong></td>
<td><strong>${sumatoria[1].teca.toFixed(2)}%</strong></td>
</tr>
</tbody>
</table>`;
let solicitudHTML =
`<figure class='highcharts-figure'>
<div id='solicitudGrafico'></div>
</figure>
<br>
<table class='table table-sm table-hover text-nowrap'>
<thead>
<tr class='text-lightblue'>
<th>Unidad administrativa</th>
<th>PAMP</th>
<th>ST-Ev</th>
<th>MC-Ev</th>
<th>MP-Ev</th>
<th>EMP</th>
</tr>
</thead>
<tbody>`;
let totalMpIndice = 0;
$.each(solicitud, function (key, sol) {
let st = sol.total - sol.mp - sol.mc;
totalMpIndice = (sol.equipoCont / sol.total) * 100;
solicitudHTML +=
`<tr>
<td>${sol.nombre}</td>
<td>${totalMpIndice.toFixed(2)}%</td>
<td>${st}</td>
<td>${sol.mc}</td>
<td>${sol.mp}</td>
<td>${sol.indice}%</td>
</tr>`;
sumatoria[2].tmc += parseInt(sol.mc);
sumatoria[2].tmp += parseInt(sol.mp);
});
sumatoria[2].temp = parseInt(sumatoria[2].tmp) / (parseInt(sumatoria[2].tmp) + parseInt(sumatoria[2].tmc)) * 100;
solicitudHTML +=
`<tr>
<td colspan="3" class="text-right"><strong>Totales estatales:    </strong></td>
<td><strong>${sumatoria[2].tmc}</strong></td>
<td><strong>${sumatoria[2].tmp}</strong></td>
<td><strong>${sumatoria[2].temp.toFixed(2)}%</strong></td>
</tr>
</tbody>
</table>`;
let evaluacionHTML =
"<figure class='highcharts-figure'>" +
"<div id='evaluacionGrafico'></div>" +
"</figure>" +
"<br>" +
"<table class='table table-sm table-hover text-nowrap'>" +
"<thead>" +
"<tr class='text-lightblue'>" +
"<th>Unidad administrativa</th>" +
"<th class='text-center'>Sol-Ev</th>" +
"<th class='text-center'>Reincidencia en fallas</th>" +
"<th class='text-center'>S. tiempos de respuesta</th>" +
"<th class='text-center'>S. solución proporcionada</th>" +
"<th class='text-center'>S. general</th>" +
"</tr>" +
"</thead>" +
"<tbody>";
$.each(evaluacion, function (key, ev) {
evaluacionHTML +=
"<tr>" +
"<td>" + ev.nombre + "</td>" +
"<td class='text-center'>" + ev.total + "</td>" +
"<td class='text-center'>" + parseFloat(ev.reincidencia).toFixed(2) + "%</td>" +
"<td class='text-center'>" + parseFloat(ev.tiempo).toFixed(2) + "%</td>" +
"<td class='text-center'>" + parseFloat(ev.solucion).toFixed(2) + "%</td>" +
"<td class='text-center'>" + parseFloat(ev.general).toFixed(2) + "%</td>" +
/*"<td class='text-center'>" + ev.reincidencia + "%</td>" +
"<td class='text-center'>" + ev.tiempo + "%</td>" +
"<td class='text-center'>" + ev.solucion + "%</td>" +
"<td class='text-center'>" + ev.general + "%</td>" +*/
"</tr>";
});
evaluacionHTML +=
"</tbody>" +
"</table>";
$("#equipo").html(equipoHTML);
$("#solicitud").html(solicitudHTML);
$("#evaluacion").html(evaluacionHTML);
$(".date-inicial").text(sumatoria[0].inicial);
$(".date-final").text(sumatoria[0].final);
$(".date-actual").text(sumatoria[0].actual);
if (equipo.length === 1) {
equipoGraficoPie(equipo[0].indice);
}
if (solicitud.length === 1) {
let mp = parseInt(solicitud[0].mp);
let mc = parseInt(solicitud[0].mc);
let total = parseInt(solicitud[0].total);
let totalMp = parseInt(solicitud[0].equipoCont);
let totalMpIndice = (totalMp / total) * 100;
let mpIndice = (mp / total) * 100;
let mcIndice = (mc / total) * 100;
let stIndice = ((total - (mp + mc)) / total) * 100;
solicitudGraficoPie(mpIndice, mcIndice, stIndice);
actualizarBarraPAMP(totalMpIndice);
}
if (evaluacion.length === 1) {
evaluacionGraficoSpider(evaluacion[0].reincidencia, evaluacion[0].tiempo, evaluacion[0].solucion, evaluacion[0].general);
}
},
error: function (data, text, error) {
let datos = jQuery.parseJSON(data.responseText);
console.log(datos);
},
complete: function () {
btn.html('<i class="fas fa-search mr-2"></i> Consultar').prop('disabled', false);
}
});
}
function actualizarBarraPAMP(porcentajePAMP) {
var barra = document.getElementById('barraPAMP');
var titulo = document.getElementById('titu');
var porcentajeFormateado = parseFloat(porcentajePAMP).toFixed(2);
// Establece la altura de la barra en porcentaje (e.g., '70%')
barra.style.height = porcentajePAMP + '%';
barra.title = '% de Atención del PAMP: ' + porcentajeFormateado + '%';
titulo.innerHTML = '<b>Atención del PAMP: ' + porcentajeFormateado + '%</b>';
$('#barra-pamp-col').show();
}
function equipoGraficoPie(indice) {
// Data retrieved from https://netmarketshare.com
Highcharts.chart('equipoGrafico', {
chart: {
plotBackgroundColor: null,
plotBorderWidth: null,
plotShadow: false,
type: 'pie'
},
title: {
text: 'Equipo actualizado'
},
tooltip: {
pointFormat: '{series.name}: <b>{point.percentage:.2f}%</b>'
},
accessibility: {
point: {
valueSuffix: '%'
}
},
plotOptions: {
pie: {
allowPointSelect: true,
cursor: 'pointer',
dataLabels: {
enabled: true,
format: '<b>{point.name}</b>: {point.percentage:.2f} %'
}
}
},
series: [{
name: 'Equipos',
colorByPoint: true,
data: [{
name: 'Con EMHV',
y: parseFloat(indice),
sliced: true,
selected: true,
color: '#00ff00'
}, {
name: 'Sin EMHV',
y: parseFloat(100 - indice),
color: '#226f04'
}]
}],
credits: {
enabled: false
}
});
}
function solicitudGraficoPie(mp, mc, st) {
Highcharts.chart('solicitudGrafico', {
chart: {
plotBackgroundColor: null,
plotBorderWidth: null,
plotShadow: false,
type: 'pie'
},
title: {
text: 'Eficacia del mantenimiento'
},
tooltip: {
pointFormat: '{series.name}: <b>{point.percentage:.2f}%</b>'
},
accessibility: {
point: {
valueSuffix: '%'
}
},
plotOptions: {
pie: {
allowPointSelect: true,
cursor: 'pointer',
dataLabels: {
enabled: true,
format: '<b>{point.name}</b>: {point.percentage:.2f} %'
}
}
},
series: [{
name: 'Solicitudes',
colorByPoint: true,
data: [{
name: 'MP-Ev',
y: parseFloat(mp),
sliced: true,
selected: true,
color: '#00ff00',
slicedOffset: 5
}, {
name: 'MC-Ev',
y: parseFloat(mc),
color: '#226f04'
}/*, {
name: 'ST-Ev',
y: parseFloat(st),
color: '#00fabf'
}*/]
}],
credits: {
enabled: false
}
});
}
function evaluacionGraficoSpider(reincidencia, tiempo, solucion, general) {
Highcharts.chart('evaluacionGrafico', {
chart: {
polar: true,
type: 'line'
},
title: null,
pane: {
size: '75%'
},
xAxis: {
categories: ['Reincidencia en fallas presentadas', 'Satisfacción en los tiempos de respuesta', 'Satisfacción en la solución proporcionada', 'Satisfacción general de los servicios'],
tickmarkPlacement: 'on',
lineWidth: 0
},
yAxis: {
gridLineInterpolation: 'polygon',
lineWidth: 0,
min: 0
},
tooltip: {
enabled: false
// shared: true,
//pointFormat: '<span style="color:{series.color}">{series.name}: <b>{point.y:,.0f}%</b><br/>'
},
legend: {
enabled: false
},
series: [{
name: 'Porcentaje',
data: [parseFloat(reincidencia), parseFloat(tiempo), parseFloat(solucion), parseFloat(general)],
dataLabels: {
enabled: true,
format: '<span>{y:.2f} %</span>',
},
pointPlacement: 'on'
}],
credits: {
enabled: false
}
});
}
</script>
{% endblock %}