From fea2ae16590fbd5da623661e70f3335358bd46c1 Mon Sep 17 00:00:00 2001 From: Jose Alberto Guerra Ugalde Date: Thu, 11 Jun 2026 21:56:02 -0600 Subject: [PATCH] - Resumen de activos en la esquina inferior izq del resumen - Correccion en la habilidad de reordenamiento - Se agregaron los activos, penalizacin y potencial al reordenamiento --- jsCode.js | 206 ++++++++++++++++++++++---------- manejoDeSesiones.js | 2 + plan_contabilizacion_activos.md | 66 ++++++++++ resumen.php | 188 +++++++++++++++++++++++++++++ 4 files changed, 401 insertions(+), 61 deletions(-) create mode 100644 plan_contabilizacion_activos.md diff --git a/jsCode.js b/jsCode.js index 9b7dc03..6d2d218 100644 --- a/jsCode.js +++ b/jsCode.js @@ -443,9 +443,6 @@ function llenaProyectoGde(proyectoNum) { $('#activoReqProyectoCant2').text(proyectos[proyectoNum].activoAsignado2) $('#activoReqProyectoCant3').text(proyectos[proyectoNum].activoAsignado3) $('#mantoGde').text('MANTENIMIENTO ' + proyectos[proyectoNum].mantenimiento) - - // Quitamos el proyecto actual del arreglo siguientesProyectos. - siguientesProyectos.splice(0, 1) } /** @@ -972,6 +969,15 @@ function redibujarTablero() { // Prioridad 3: Si no hay ninguno 'En proceso', tomar el primero de la cola 'En siguientes'. if (!nuevoProyectoGde) { nuevoProyectoGde = proyectos.find((p) => p.estatus === 'En siguientes') + if (nuevoProyectoGde) { + let index = siguientesProyectos.findIndex( + (p) => p.id === nuevoProyectoGde.id + ) + if (index !== -1) { + siguientesProyectos.splice(index, 1) + sincronizarColaSiguientes() + } + } } // 2. Limpiar todos los slots de proyectos chicos. @@ -1980,7 +1986,7 @@ $('#accionHabilidadEspecial').click(function () { guardaSesion() } } else if (accionQuincenal === 'Ordenar los próximos 5 proyectos') { - abrirDialogoOrdenarProyectos(elArreglo, quincenaActual); + abrirDialogoOrdenarProyectos(elArreglo, quincenaActual) } else if (accionQuincenal === 'Omitir un mantenimiento') { // Actualizamos lastUsed setArrayValue(elArreglo, 'accionEspecialLastUsed', quincenaActual) @@ -3373,6 +3379,8 @@ function continuamos() { obtenerProyectosPendientes() if (siguientesProyectos.length > 0) { llenaProyectoGde(siguientesProyectos[0].id) + siguientesProyectos.splice(0, 1) + sincronizarColaSiguientes() } } else { // JUEGO CARGADO @@ -3431,7 +3439,17 @@ function continuamos() { // 4. Reconstruir la cola de 'siguientesProyectos'. // Primero, tomar los que ya estaban 'En siguientes' del estado guardado. - siguientesProyectos = proyectos.filter((p) => p.estatus === 'En siguientes') + let tempSiguientes = proyectos.filter((p) => p.estatus === 'En siguientes') + if (arregloGeneral.siguientesProyectosIds) { + tempSiguientes.sort((a, b) => { + let idxA = arregloGeneral.siguientesProyectosIds.indexOf(a.id) + let idxB = arregloGeneral.siguientesProyectosIds.indexOf(b.id) + if (idxA === -1) idxA = 999 + if (idxB === -1) idxB = 999 + return idxA - idxB + }) + } + siguientesProyectos = tempSiguientes // Luego, completar la cola con proyectos 'Pendiente' si es necesario. const proyectosPendientes = proyectos.filter( @@ -3455,6 +3473,7 @@ function continuamos() { siguientesProyectos.push(proyectoElegido) proyectosDisponibles.splice(randomIndex, 1) } + sincronizarColaSiguientes() // 5. Llenar el proyecto grande. if (proyectoGde) { @@ -3462,6 +3481,8 @@ function continuamos() { } else if (siguientesProyectos.length > 0) { // Si no había ningún proyecto en el tablero, tomar el primero de la cola. llenaProyectoGde(siguientesProyectos[0].id) + siguientesProyectos.splice(0, 1) + sincronizarColaSiguientes() } } @@ -3696,82 +3717,145 @@ window.confirm = function (message, callback, customWidth) { }) } +function sincronizarColaSiguientes() { + if (typeof arregloGeneral !== 'undefined' && siguientesProyectos) { + arregloGeneral.siguientesProyectosIds = siguientesProyectos.map((p) => p.id) + } +} + function abrirDialogoOrdenarProyectos(elArreglo, quincenaActual) { - let proyectosAOrdenar = siguientesProyectos.slice(0, 5); - let proyectosRestantes = siguientesProyectos.slice(5); + let proyectosAOrdenar = siguientesProyectos.slice(0, 5) + let proyectosRestantes = siguientesProyectos.slice(5) - let container = $('
'); - container.append('

Arrastra los proyectos para ordenar la prioridad en la que aparecerán:

'); + let container = $('
') + container.append( + '

Arrastra los proyectos para ordenar la prioridad en la que aparecerán:

' + ) + + let list = $( + '' + ) + const claseMapa = { + amarillo: 'actPersonalCalificado', + naranjas: 'actAnalistas', + verdes: 'actFlujoDeEfectivo', + azules: 'actTransporteYMaquinaria', + rojos: 'actTecnologia', + morados: 'actCreditoEInversion', + } + const colores = [ + 'amarillo', + 'naranjas', + 'verdes', + 'azules', + 'rojos', + 'morados', + ] - let list = $(''); proyectosAOrdenar.forEach((p, idx) => { + let activosReqHtml = '' + colores.forEach((color) => { + const valor = p[color] + if (valor !== null && valor > 0) { + const claseActivo = claseMapa[color] + const textColor = + color === 'amarillo' || color === 'naranjas' || color === 'verdes' + ? 'black' + : 'white' + activosReqHtml += `${valor}` + } + }) + + const penVal = parseInt(p.penalizacion) || 0 + const penalizacionFormateada = + penVal > 0 ? '$' + new Intl.NumberFormat('es-MX').format(penVal) : 'N/A' + let item = $(` -
  • - ${idx + 1}. ${p.nombreProyecto} - ${p.departamento} +
  • +
    + ${idx + 1}. ${p.nombreProyecto} + ${p.departamento} +
    +
    +
    + ${activosReqHtml} +
    +
    + Potencial: ${p.potencial || 'N/A'}
    + Penalización: ${penalizacionFormateada} +
    +
  • - `); - list.append(item); - }); - container.append(list); + `) + list.append(item) + }) + container.append(list) container.dialog({ modal: true, title: 'Ordenar Próximos 5 Proyectos', width: 500, - open: function() { + open: function () { list.sortable({ - placeholder: "ui-state-highlight", + placeholder: 'ui-state-highlight', forcePlaceholderSize: true, - update: function(event, ui) { - list.find('li').each(function(index) { - let nombre = $(this).find('span').first().text(); - nombre = nombre.replace(/^\d+\.\s*/, ''); - $(this).find('span').first().text((index + 1) + '. ' + nombre); - }); - } - }); - list.disableSelection(); - $('').appendTo('head'); + update: function (event, ui) { + list.find('li').each(function (index) { + let nombre = $(this).find('span').first().text() + nombre = nombre.replace(/^\d+\.\s*/, '') + $(this) + .find('span') + .first() + .text(index + 1 + '. ' + nombre) + }) + }, + }) + list.disableSelection() + $( + '' + ).appendTo('head') }, - close: function() { - $('#sortable-style').remove(); - $(this).dialog('destroy').remove(); + close: function () { + $('#sortable-style').remove() + $(this).dialog('destroy').remove() }, buttons: { - "Guardar Orden": function() { - let nuevoOrdenIds = []; - list.find('li').each(function() { - nuevoOrdenIds.push(parseInt($(this).data('id'))); - }); + 'Guardar Orden': function () { + let nuevoOrdenIds = [] + list.find('li').each(function () { + nuevoOrdenIds.push(parseInt($(this).data('id'))) + }) - let nuevosSiguientes = []; - nuevoOrdenIds.forEach(id => { - let proj = proyectos.find(p => p.id === id); - if (proj) nuevosSiguientes.push(proj); - }); - - nuevosSiguientes = nuevosSiguientes.concat(proyectosRestantes); - siguientesProyectos = nuevosSiguientes; + let nuevosSiguientes = [] + nuevoOrdenIds.forEach((id) => { + let proj = proyectos.find((p) => p.id === id) + if (proj) nuevosSiguientes.push(proj) + }) - proyectos.forEach(p => { - if (p.estatus === 'En siguientes') p.estatus = 'Pendiente'; - }); - siguientesProyectos.forEach(p => { - let proj = proyectos.find(pr => pr.id === p.id); - if (proj) proj.estatus = 'En siguientes'; - }); + nuevosSiguientes = nuevosSiguientes.concat(proyectosRestantes) + siguientesProyectos = nuevosSiguientes + sincronizarColaSiguientes() - setArrayValue(elArreglo, 'accionEspecialLastUsed', quincenaActual); - var audio = new Audio('./Microwave_bell.wav'); - audio.play(); - window.alert('¡Orden de proyectos actualizado con éxito!'); + proyectos.forEach((p) => { + if (p.estatus === 'En siguientes') p.estatus = 'Pendiente' + }) + siguientesProyectos.forEach((p) => { + let proj = proyectos.find((pr) => pr.id === p.id) + if (proj) proj.estatus = 'En siguientes' + }) - if (typeof guardaSesion === 'function') guardaSesion(); - $(this).dialog('close'); + setArrayValue(elArreglo, 'accionEspecialLastUsed', quincenaActual) + var audio = new Audio('./Microwave_bell.wav') + audio.play() + window.alert('¡Orden de proyectos actualizado con éxito!') + + if (typeof guardaSesion === 'function') guardaSesion() + $(this).dialog('close') }, - "Cancelar": function() { $(this).dialog('close'); } - } - }); + Cancelar: function () { + $(this).dialog('close') + }, + }, + }) } // diff --git a/manejoDeSesiones.js b/manejoDeSesiones.js index b9ef5aa..280b766 100644 --- a/manejoDeSesiones.js +++ b/manejoDeSesiones.js @@ -164,6 +164,8 @@ function guardarDatosEnServidor() { .catch((error) => console.error('Error:', error)) } +const guardaSesion = guardarDatosEnServidor; + // Cargar datos al iniciar la página document.addEventListener('DOMContentLoaded', cargarDatosDesdeServidor) diff --git a/plan_contabilizacion_activos.md b/plan_contabilizacion_activos.md new file mode 100644 index 0000000..bbf92df --- /dev/null +++ b/plan_contabilizacion_activos.md @@ -0,0 +1,66 @@ +# Plan de Implementación: Contabilización de Activos (Producidos, Recuperados y Perdidos) + +Este documento detalla la lógica matemática y los pasos técnicos necesarios para implementar la visualización y el cálculo de estadísticas de activos en la pantalla de resumen de la aplicación **Colaborando**. + +--- + +## 📋 Resumen del Enfoque + +Para evitar desincronizaciones de datos y la necesidad de alterar la base de datos o los esquemas JSON de persistencia de sesión, utilizaremos un **cálculo dinámico en el cliente** (JavaScript). Toda la información necesaria ya existe dentro del arreglo global `proyectos` y el objeto `arregloGeneral`, que se guardan y cargan automáticamente con la sesión. + +--- + +## 🧮 Lógica de Cálculo de Métricas + +Para cada tipo de activo (p. ej., *Personal Calificado*, *Analistas*, *Flujo de Efectivo*, etc.), los valores se calculan recorriendo el arreglo global `proyectos` buscando proyectos con `estatus === 'Completado'`: + +### 1. Activos Producidos (Total) +* **Definición:** Activos que fueron creados/asignados para completar los proyectos finalizados. +* **Lógica:** Suma de los requerimientos originales de todos los proyectos completados. +* **Fórmula:** + $$\text{Producidos}(c) = \sum_{p \in \text{Completados}} p[c]$$ + *(Donde $c$ es el campo de requerimiento del activo, como `amarillo`, `naranjas`, etc.)* + +### 2. Activos Recuperados +* **Definición:** Activos que se arrastraron exitosamente desde los proyectos terminados hacia los departamentos (lo cual resta del proyecto y suma al departamento). +* **Lógica:** La diferencia entre el requerimiento original del proyecto completado y su valor de asignación actual (que disminuye al ser arrastrado). +* **Fórmula:** + $$\text{Recuperados}(c) = \sum_{p \in \text{Completados}} (p[c] - p['activoAsignado' + (i+1)])$$ + *(Donde $i$ es el índice del slot correspondiente al tipo de activo en el proyecto)* + +### 3. Activos Perdidos +* **Definición:** Activos que no se pudieron recuperar de proyectos finalizados por falta de recursos o de espacio antes de cambiar de periodo. +* **Lógica:** Suma de los activos remanentes (`activoAsignadoX`) en proyectos completados que pertenecen a trimestres anteriores (ya que en el trimestre actual todavía es posible recuperarlos). +* **Fórmula:** + $$\text{Perdidos}(c) = \sum_{p \in \text{Completados}, p.\text{trimestreCompletado} < \text{trimestreActual}} p['activoAsignado' + (i+1)]$$ + +--- + +## 🛠️ Archivos y Cambios Requeridos + +Todo el cambio se centraliza en el archivo **[resumen.php](file:///C:/CHV/ludotecaLocal/Colaborando/resumen.php)**. + +### Paso 1: Interfaz de Usuario (HTML) +Modificar la estructura de la tabla `#tablaActivos` (líneas ~308-348) para cambiar sus encabezados a: +1. **Activo** +2. **Producidos** +3. **Recuperados** +4. **Perdidos** + +Asignar identificadores de celda descriptivos para inyectar los datos, por ejemplo: +* Para Personal Calificado: `producidosPersonalCalificado`, `recuperadosPersonalCalificado`, `perdidosPersonalCalificado`. +* Para Analistas: `producidosAnalistas`, `recuperadosAnalistas`, `perdidosAnalistas`. +* *(Repetir para los 6 tipos de activos)*. + +### Paso 2: Motor de Cálculo (JavaScript) +Dentro de la función `actualizarResumenUI()` (línea ~720): +1. **Inicializar:** Crear un objeto local para llevar las cuentas por tipo de activo e indicador (ej. `const stats = { PersonalCalificado: { producidos: 0, recuperados: 0, perdidos: 0 }, ... }`). +2. **Procesar:** Iterar sobre el arreglo `proyectos` y clasificar según las fórmulas explicadas. +3. **Renderizar:** Actualizar el contenido de las celdas del HTML utilizando jQuery (ej. `$('#producidosPersonalCalificado').text(stats.PersonalCalificado.producidos)`). + +--- + +## 🎯 Beneficios de esta Solución +* **Integridad de datos:** No requiere modificar la estructura JSON del archivo de datos (`datos.json` / `cheve.json`). +* **Sincronización total:** Si el usuario recarga la página o arrastra activos, la tabla se vuelve a calcular en tiempo real reflejando el estado real del juego. +* **Mantenibilidad:** El código queda autocontenido en la vista de resumen. diff --git a/resumen.php b/resumen.php index fe532ff..0602224 100644 --- a/resumen.php +++ b/resumen.php @@ -391,6 +391,57 @@ +
    +

    CONTABILIZACIÓN DE ACTIVOS

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    ActivoProducidosRecuperadosPerdidos
    Personal calificado000
    Analistas000
    Flujo de efectivo000
    Crédito e Inversión000
    Tecnología000
    Transporte y Maquinaria000
    +