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
+
+
+
+ | Activo |
+ Producidos |
+ Recuperados |
+ Perdidos |
+
+
+
+
+ | Personal calificado |
+ 0 |
+ 0 |
+ 0 |
+
+
+ | Analistas |
+ 0 |
+ 0 |
+ 0 |
+
+
+ | Flujo de efectivo |
+ 0 |
+ 0 |
+ 0 |
+
+
+ | Crédito e Inversión |
+ 0 |
+ 0 |
+ 0 |
+
+
+ | Tecnología |
+ 0 |
+ 0 |
+ 0 |
+
+
+ | Transporte y Maquinaria |
+ 0 |
+ 0 |
+ 0 |
+
+
+
+