diff --git a/colaborando_arreglos.js b/colaborando_arreglos.js index b21626a..b4183f1 100644 --- a/colaborando_arreglos.js +++ b/colaborando_arreglos.js @@ -6,6 +6,12 @@ let arregloGeneral = { trimestre2: 0, trimestre3: 0, trimestre4: 0, + objetivoT1: 100000, + objetivoT2: 100000, + objetivoT3: 100000, + objetivoT4: 100000, + bAccion1: 0, + bAccion2: 0, } let arreglosCapacitaciones = {} diff --git a/doc/PROYECTO.md b/doc/PROYECTO.md index 0ee65a4..0a2ca19 100644 --- a/doc/PROYECTO.md +++ b/doc/PROYECTO.md @@ -59,8 +59,12 @@ Los activos producidos por proyectos completados se dividen en 6 tipos: *Persona * **16/Jul/2026:** * **Cambio de Desechar a Penalizar:** En la pantalla de resumen, se cambió el botón "Desechar" por "Penalizar". * **Persistencia de Actividad:** Los proyectos penalizados ahora se mantienen activos (`En proceso`) en lugar de descartarse. - * **Actualización de Visualización:** Tanto al pagar mantenimiento como al penalizar, el proyecto permanece en la quincena actual; en el resumen, su tarjeta cambia a color gris y sus botones se bloquean. Se excluyeron de este bloqueo los proyectos con mantenimiento "N/A" (como "Capacitación Brigadas") y las Incidencias. + * **Actualización de Visualización y Penalización General:** Tanto al pagar mantenimiento como al penalizar, el proyecto permanece en la quincena actual; en el resumen, su tarjeta cambia a color gris y sus botones se bloquean. El botón de **Penalizar** está habilitado en **todos** los proyectos e Incidencias activas durante el fin de trimestre, deduciendo la penalización del acumulado general. * **Descuentos de Habilidades en Mantenimiento:** Al pagar mantenimiento de forma normal, se aplican correctamente los beneficios/descuentos de recursos ganados por el departamento mediante sus habilidades compradas (`habilidadTiempo`, `habilidadPersonal`, `habilidadCapital`), reduciendo el costo cobrado en base a los beneficios definidos. + * **Corrección de Proyectos Descartados en `TablaProyectoGde`:** Se corrigió un error en `continuamos()` en [jsCode.js](file:///c:/CHV/ludotecaLocal/Colaborando/jsCode.js#L3435) que provocaba que un proyecto descartado que estuviera seleccionado en el cuadro grande (`TablaProyectoGde`) se reasignara por error al volver al tablero desde el resumen. Ahora la inicialización del tablero valida estrictamente que solo se carguen proyectos `En proceso`, removiendo el proyecto descartado inmediatamente sin necesidad de refrescar la página. + * **Persistencia de Botones de Acción 1 y 2 (`bAccion1` y `bAccion2`):** Se integró el estado de las acciones en `arregloGeneral.bAccion1` y `arregloGeneral.bAccion2`. Al hacer clic en los botones 1 y 2 o reiniciarse con el timer, se guarda el estado en el servidor y al volver del resumen a la vista del tablero se restaura automáticamente la selección activa (fondo azul y texto blanco). + * **Gráfica Mixta de Progresión (Barras + Línea) con Zoom 2.5X y Posicionamiento Ajustado:** Integración de gráfica HTML5 Canvas sin dependencias en el Bloque 3 de resumen. Representa los Objetivos del Trimestre (`arregloGeneral.objetivoT1..T4`, por defecto $100,000) mediante barras azules e Ingresos Reales mediante una línea roja con nodos. El panel flotante emergente desplegado en `mouseenter` se ajustó a escala **2.5X ampliado** (560px × 185px) y se reubicó dinámicamente sobre la tarjeta del Bloque 3, evitando recortes en el borde superior de la ventana del navegador. Se corrigió el alineamiento de texto de la leyenda (`textAlign = 'left'`) para que los textos "Objetivo Trimestre" e "Ingreso Real" no se solapen con sus respectivos iconos. + * **Redistribución de Layout en Resumen:** Se reestructuró la vista en 2 filas: Fila superior con 5 columnas horizontales para métricas/gráficos/controles y Fila inferior con Departamentos (20%), Proyectos Terminados (40%) y Proyectos Activos (40%). * **03/Jul/2026:** * Implementación de la ventana de mantenimiento trimestral (Q6, 12, 18, 24) en resumen y tablero. * Integración de habilidades especiales para omitir costo de mantenimiento (Logística) y omitir penalizaciones por desecho (Operaciones). diff --git a/doc/walkthrough.md b/doc/walkthrough.md index 26f6535..d0245c7 100644 --- a/doc/walkthrough.md +++ b/doc/walkthrough.md @@ -5,15 +5,19 @@ Se ha implementado la funcionalidad solicitada para gestionar proyectos activos ## Cambios Realizados ### 1. Pantalla de Resumen ([resumen.php](file:///c:/CHV/ludotecaLocal/Colaborando/resumen.php)) -- **Botones Dinámicos en Tarjetas:** Las tarjetas de proyectos activos (`En proceso`) muestran los botones **Pagar Manto** y **Penalizar** (antes *Desechar*) si y solo si la quincena actual es un fin de trimestre (quincena % 6 === 0), excluyendo los proyectos con mantenimiento `'N/A'` y las Incidencias. +- **Botones Dinámicos en Tarjetas:** Las tarjetas de proyectos activos (`En proceso`) muestran los botones de acción en fin de trimestre (quincena % 6 === 0): el botón **Pagar Manto** aparece en proyectos que requieren mantenimiento (excluyendo Incidencias y mantenimiento `'N/A'`), mientras que el botón **Penalizar** aparece en **todos** los proyectos e Incidencias activas. - **Persistencia de Proyectos:** El botón "Penalizar" ya no descarta el proyecto (no cambia su estatus a `descartado`). El proyecto permanece activo (`En proceso`). -- **Estado Deshabilitado:** Una vez que se paga el mantenimiento o se paga la penalización, la tarjeta del proyecto se tiñe de color gris en la pantalla de resumen, y los botones **Pagar Manto** y **Penalizar** quedan deshabilitados (`disabled`), indicando visualmente que ya se resolvió la situación del proyecto para el trimestre actual (los proyectos exentos o Incidencias permanecen activos y con su color blanco normal). +- **Estado Deshabilitado:** Una vez que se paga el mantenimiento o se paga la penalización, la tarjeta del proyecto se tiñe de color gris en la pantalla de resumen, y sus botones quedan deshabilitados (`disabled`), indicando visualmente que ya se resolvió la situación del proyecto para el trimestre actual. - **Descuentos por Habilidades:** Al realizar el pago de mantenimiento de forma normal, se aplican los descuentos de recursos (Tiempo, Personal, Capital) ganados mediante las habilidades compradas del departamento (`habilidadTiempo`, `habilidadPersonal`, `habilidadCapital`), restando del total del depto el costo neto descontado (mínimo de 0). - **Textos e Interfaz:** Se ajustó la confirmación de penalización a: *"¿Estás seguro de que quieres pagar la penalización del proyecto..."*. Se detalla el costo original y costo neto final con descuento en el confirm del pago de mantenimiento. - **Habilidad Especial de Operaciones:** Permite aplicar la penalización del proyecto sin costo utilizando la habilidad especial (actualizando su cooldown). +- **Redistribución de Layout (5 Columnas Superiores + Fila Inferior 20/40/40%):** Se reorganizó la vista de resumen en 2 filas. La fila superior (`top-row`) distribuye los 5 bloques de control/información horizontalmente en 5 columnas de igual proporción. La fila inferior (`main-container`) ubica Departamentos a la izquierda (20%), Proyectos Terminados al centro (40%) y Proyectos Activos a la derecha (40%). +- **Gráfica Mixta de Progresión (Barras + Líneas) con Zoom Flotante 2.5X y Persistencia:** En la parte inferior del Bloque 3 (Progresión), se integró una gráfica Canvas HTML5 que representa los Objetivos del Trimestre (almacenados en la sesión como `arregloGeneral.objetivoT1..T4`, por defecto $100,000) en **barras azules** y los Ingresos Reales por Trimestre (T1..T4) en una **línea roja con nodos y valores**. Al colocar el cursor sobre la gráfica (`mouseenter`), se despliega un panel emergente flotante alineado sobre la tarjeta del bloque con la gráfica en tamaño **2.5X ampliado** con posicionamiento dinámico anti-recorte y leyenda ajustada a la izquierda (`textAlign = 'left'`) para evitar cualquier solapamiento con los iconos. Se oculta al retirar el cursor (`mouseleave`). ### 2. Tablero de Juego ([jsCode.js](file:///c:/CHV/ludotecaLocal/Colaborando/jsCode.js)) - **Visibilidad Permanente:** Se eliminaron los filtros que ocultaban el proyecto del tablero chico o grande cuando el mantenimiento ya había sido resuelto. El proyecto permanece visible tanto en el tablero como en el resumen durante la quincena actual, permitiendo seguir trabajando en él. +- **Corrección de Proyectos Descartados (`continuamos()`):** Se actualizó la carga inicial del tablero para verificar estrictamente que el proyecto asignado a `TablaProyectoGde` tenga estatus `'En proceso'`. Si un proyecto fue descartado desde el resumen o mediante habilidad especial, al retornar al tablero ya no se cargará en `TablaProyectoGde` y desaparecerá de inmediato sin necesidad de refrescar la página. +- **Persistencia de Botones de Acción (`bAccion1` y `bAccion2`):** Se guardan los estados de selección (activado/desactivado) de las acciones 1 y 2 en `arregloGeneral.bAccion1` y `arregloGeneral.bAccion2` al pulsar `toggleColor()` y al finalizar el timer. Al navegar entre el tablero y el resumen y regresar, las acciones permanecen seleccionadas tal como las dejó el usuario. ## Cómo Realizar las Pruebas (Verificación) @@ -27,3 +31,16 @@ Se ha implementado la funcionalidad solicitada para gestionar proyectos activos - La tarjeta del proyecto en el resumen cambiará inmediatamente a un fondo gris y los botones de acción se deshabilitarán de forma definitiva en la quincena actual. - Ve al Tablero (`colaborando2.php`) en la misma quincena (Q6) y verifica que el proyecto **sigue estando visible** en el tablero chico y en el slot de proyecto grande. - Avanza la quincena a **Q7** (Trimestre 2). Vuelve a la pantalla de resumen y verifica que el proyecto recupera su color blanco y los botones ya no aparecen (pues no es fin de trimestre), y que en el tablero continúa disponible con total normalidad. + +2. **Escenario de Descarte de Proyecto Seleccionado:** + - Selecciona un proyecto en `TablaProyectoGde` en el tablero. + - Ve al resumen (o usa la Habilidad Especial de Operaciones para "Descartar un proyecto"). + - Descarta el proyecto que estaba seleccionado en `TablaProyectoGde`. + - Pulsa "Ir al Tablero". + - Verifica que el proyecto descartado **ya no aparece** en `TablaProyectoGde` ni en el tablero chico sin necesidad de refrescar la página. + +3. **Escenario de Persistencia de Acciones 1 y 2:** + - En el tablero, activa los botones de acción `1` (`bAccion1`) o `2` (`bAccion2`). + - Navega al Resumen haciendo clic en "Ir al Resumen". + - Regresa al Tablero haciendo clic en "Ir al Tablero". + - Verifica que las acciones seleccionadas se mantienen activas (color azul y texto blanco). diff --git a/jsCode.js b/jsCode.js index b9dde75..1c41819 100644 --- a/jsCode.js +++ b/jsCode.js @@ -3350,6 +3350,37 @@ function continuamos() { $('#nombreSesion').text('Sesión: ' + arregloGeneral.nombreSesion) } + // Restablecer el estado de los botones de acción (bAccion1 y bAccion2) + if (typeof arregloGeneral !== 'undefined') { + if (parseInt(arregloGeneral.bAccion1) === 1) { + $('#bAccion1').css({ 'background-color': '#068bc3', 'color': 'white' }) + if (document.getElementById('bAccion1')) { + document.getElementById('bAccion1').style.backgroundColor = '#068bc3' + document.getElementById('bAccion1').style.color = 'white' + } + } else { + $('#bAccion1').css({ 'background-color': '', 'color': 'black' }) + if (document.getElementById('bAccion1')) { + document.getElementById('bAccion1').style.backgroundColor = '' + document.getElementById('bAccion1').style.color = 'black' + } + } + + if (parseInt(arregloGeneral.bAccion2) === 1) { + $('#bAccion2').css({ 'background-color': '#068bc3', 'color': 'white' }) + if (document.getElementById('bAccion2')) { + document.getElementById('bAccion2').style.backgroundColor = '#068bc3' + document.getElementById('bAccion2').style.color = 'white' + } + } else { + $('#bAccion2').css({ 'background-color': '', 'color': 'black' }) + if (document.getElementById('bAccion2')) { + document.getElementById('bAccion2').style.backgroundColor = '' + document.getElementById('bAccion2').style.color = 'black' + } + } + } + asignarHabilidades('RecursosHumanos') asignarHabilidades('MarketingYVentas') asignarHabilidades('Finanzas') @@ -3425,21 +3456,28 @@ function continuamos() { const quincenaCargada = parseInt(arregloGeneral.quincena) || 1 const trimestreActual = Math.floor((quincenaCargada - 1) / 6) + 1 - // 1. Usar el ID del proyecto grande que se guardó en la sesión, si no está completado. + // 1. Usar el ID del proyecto grande que se guardó en la sesión, únicamente si sigue 'En proceso'. let proyectoGde = null console.log('ID de proyecto grande guardado:', proyectoGdeActual) if ( proyectoGdeActual !== undefined && proyectoGdeActual !== null && proyectoGdeActual !== '' && - proyectos[proyectoGdeActual].estatus !== 'Completado' + proyectos[proyectoGdeActual] && + proyectos[proyectoGdeActual].estatus === 'En proceso' ) { proyectoGde = proyectos[proyectoGdeActual] } else { - // Fallback por si no se encuentra un ID guardado o si estaba completado. - proyectoGde = proyectos.find( - (p) => p.estatus === 'En proceso' || p.estatus === 'En siguientes' - ) + // Fallback por si no se encuentra un ID guardado o si estaba completado o descartado. + proyectoGde = proyectos.find((p) => p.estatus === 'En proceso') + if (!proyectoGde) { + proyectoGde = proyectos.find((p) => p.estatus === 'En siguientes') + } + if (proyectoGde) { + proyectoGdeActual = proyectos.findIndex((p) => p.id === proyectoGde.id) + } else { + proyectoGdeActual = '' + } } // 2. Limpiar los slots de proyectos chicos primero. @@ -3497,14 +3535,17 @@ function continuamos() { } sincronizarColaSiguientes() - // 5. Llenar el proyecto grande. - if (proyectoGde) { + // 5. Llenar o limpiar el proyecto grande. + if (proyectoGde && (proyectoGde.estatus === 'En proceso' || proyectoGde.estatus === 'En siguientes')) { llenaProyectoGde(proyectoGde.id) } 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() + } else { + limpiarProyectoGde() + proyectoGdeActual = '' } } @@ -3658,11 +3699,25 @@ function relojUpdateTime() { function relojPlaySound() { const audio = new Audio('Microwave_bell.wav') - if ($('#bAccion2').css('background-color') === 'rgb(6, 139, 195)') { - $('#bAccion1').css('background-color', '') - $('#bAccion1').css('color', 'black') - $('#bAccion2').css('background-color', '') - $('#bAccion2').css('color', 'black') + const bg2 = $('#bAccion2').css('background-color') + if (bg2 === 'rgb(6, 139, 195)' || bg2 === '#068bc3') { + $('#bAccion1').css({ 'background-color': '', 'color': 'black' }) + $('#bAccion2').css({ 'background-color': '', 'color': 'black' }) + if (document.getElementById('bAccion1')) { + document.getElementById('bAccion1').style.backgroundColor = '' + document.getElementById('bAccion1').style.color = 'black' + } + if (document.getElementById('bAccion2')) { + document.getElementById('bAccion2').style.backgroundColor = '' + document.getElementById('bAccion2').style.color = 'black' + } + if (typeof arregloGeneral !== 'undefined') { + arregloGeneral.bAccion1 = 0 + arregloGeneral.bAccion2 = 0 + if (typeof guardaSesion === 'function') { + guardaSesion() + } + } } audio.play() } @@ -3684,13 +3739,29 @@ function relojResumeTimer() { } function toggleColor(button) { - console.log(button.style.backgroundColor) - if (button.style.backgroundColor === 'rgb(6, 139, 195)') { - button.style.backgroundColor = '' // Restablecer al color original - button.style.color = 'black' // Cambiar el texto a negro + const $btn = $(button) + const id = $btn.attr('id') + const bg = $btn.css('background-color') + const isSelected = (bg === 'rgb(6, 139, 195)' || bg === '#068bc3' || button.style.backgroundColor === 'rgb(6, 139, 195)' || button.style.backgroundColor === '#068bc3') + + if (isSelected) { + $btn.css({ 'background-color': '', 'color': 'black' }) + button.style.backgroundColor = '' + button.style.color = 'black' + if (id && typeof arregloGeneral !== 'undefined') { + arregloGeneral[id] = 0 + } } else { - button.style.backgroundColor = '#068bc3' // Cambiar a azul - button.style.color = 'white' // Cambiar el texto a blanco + $btn.css({ 'background-color': '#068bc3', 'color': 'white' }) + button.style.backgroundColor = '#068bc3' + button.style.color = 'white' + if (id && typeof arregloGeneral !== 'undefined') { + arregloGeneral[id] = 1 + } + } + + if (typeof guardaSesion === 'function') { + guardaSesion() } } diff --git a/manejoDeSesiones.js b/manejoDeSesiones.js index 0b7e911..2c3896e 100644 --- a/manejoDeSesiones.js +++ b/manejoDeSesiones.js @@ -71,6 +71,24 @@ function cargarDatosDesdeServidor() { if (typeof arregloGeneral.trimestre4 === 'undefined') { arregloGeneral.trimestre4 = 0 } + if (typeof arregloGeneral.objetivoT1 === 'undefined') { + arregloGeneral.objetivoT1 = 100000 + } + if (typeof arregloGeneral.objetivoT2 === 'undefined') { + arregloGeneral.objetivoT2 = 100000 + } + if (typeof arregloGeneral.objetivoT3 === 'undefined') { + arregloGeneral.objetivoT3 = 100000 + } + if (typeof arregloGeneral.objetivoT4 === 'undefined') { + arregloGeneral.objetivoT4 = 100000 + } + if (typeof arregloGeneral.bAccion1 === 'undefined') { + arregloGeneral.bAccion1 = 0 + } + if (typeof arregloGeneral.bAccion2 === 'undefined') { + arregloGeneral.bAccion2 = 0 + } // --- FIN DEL PARCHE --- arreglosCapacitaciones = data.data.arreglosCapacitaciones arreglosActivos = data.data.arreglosActivos diff --git a/resumen.php b/resumen.php index 45ae080..8778a7c 100644 --- a/resumen.php +++ b/resumen.php @@ -27,10 +27,33 @@ } /* --- Estilos para la nueva maqueta --- */ + .dashboard-wrapper { + display: flex; + flex-direction: column; + gap: 8px; + padding: 8px; + } + + .top-row { + display: flex; + gap: 8px; + width: 100%; + } + + .top-row>.card { + flex: 1 1 0px; + margin: 0; + min-width: 0; + padding: 4px; + display: flex; + flex-direction: column; + justify-content: flex-start; + } + .main-container { display: flex; - gap: 15px; - padding: 10px; + gap: 8px; + width: 100%; } .column { @@ -39,37 +62,36 @@ gap: 5px; } - .column-left { - flex: 1 1 0px; - /* 1/5 del ancho */ - } - .column-depts { flex: 1 1 0px; - /* 1/5 del ancho */ + min-width: 0; + /* 1/5 del ancho (20%) */ } .column-completed { - flex: 1 1 0px; - /* 1/5 del ancho */ + flex: 2 1 0px; + min-width: 0; + /* 2/5 del ancho (40%) */ } .column-active { flex: 2 1 0px; - /* 2/5 del ancho */ + min-width: 0; + /* 2/5 del ancho (40%) */ } .card { border: 2px solid #ccc; border-radius: 5px; - padding: 5px; + padding: 6px; background-color: #f9f9f9; } h2 { text-align: center; margin-top: 0; - font-size: 1.2em; + margin-bottom: 6px; + font-size: 1.25em; background-color: gray; color: white; padding: 5px; @@ -78,13 +100,13 @@ /* Estilos para las tablas de resumen */ .summary-table { - font-size: 12px; + font-size: 13px; } .summary-table th, .summary-table td { border: 1px solid #ddd; - padding: 4px; + padding: 4px 6px; text-align: center; } @@ -244,69 +266,69 @@ - - -
- +
-
+
+ +
- -
-
+
+ +
+
+
- - Ir al Tablero -
-
-
-
- - - - - -
- - -   - - - -
2:00
-
- - - -
- -
2 min
-
- -
-
- - - - - - - -
1234
-
-
- - - - - - -
+ + Ir al
Tablero
+
+
+
+ + + + + +
+ +
+ +
+
2:00
+
+ + +
+ +
2 min
+
+ +
+
+ + + + + + + +
1234
+
+
+ + + + + + +
+
+ +
@@ -318,43 +340,43 @@ - + - + - + - + - + - +
Personal calificadoPersonal calificado
AnalistasAnalistas
Flujo de efectivoFlujo de efectivo
Crédito e InversiónCrédito e Inversión
TecnologíaTecnología
Transporte y MaquinariaTransporte y Maquinaria
+ +

PROGRESIÓN

- -
- +
@@ -366,14 +388,14 @@ - + - + @@ -381,19 +403,25 @@
IngresoIngreso $0 $0 $0 $0
AcumuladoAcumulado $0 $0 $0
+
+ + +
+ +
-

PROYECTOS

+

PROYECTOS E INCIDENCIAS

- -
-
-
-

INCIDENCIAS

- - +
+
+ +

CONTABILIZACIÓN DE ACTIVOS

@@ -445,305 +473,310 @@
+
-
-

DEPARTAMENTOS

+
- -
- - - - - - - - - - - - -
MARKETING Y VENTAS - 0 - 0 -
- - - - - - - - - - - -
- -
0
-
- -
0
-
- -
0
-
-
+ +
+

DEPARTAMENTOS

+ + +
+ + + + + + + + + + + + +
MARKETING Y VENTAS + 0 + 0 +
+ + + + + + + + + + + +
+ +
0
+
+ +
0
+
+ +
0
+
+
+
+ + +
+ + + + + + + + + + + + +
FINANZAS + 0 + 0 +
+ + + + + + + + + + + +
+ +
0
+
+ +
0
+
+ +
0
+
+
+
+ + +
+ + + + + + + + + + + + +
INVESTIGACIÓN Y DESARROLLO + 0 + 0 +
+ + + + + + + + + + + +
+ +
0
+
+ +
0
+
+ +
0
+
+
+
+ + +
+ + + + + + + + + + + + +
LOGÍSTICA Y SISTEMAS + 0 + 0 +
+ + + + + + + + + + + +
+ +
0
+
+ +
0
+
+ +
0
+
+
+
+ + +
+ + + + + + + + + + + + +
OPERACIONES + 0 + 0 +
+ + + + + + + + + + + +
+ +
0
+
+ +
0
+
+ +
0
+
+
+
+ + +
+ + + + + + + + + + + + +
RECURSOS HUMANOS + 0 + 0 +
+ + + + + + + + + + + +
+ +
0
+
+ +
0
+
+ +
0
+
+
+
+ + +
+ +
- -
- - - - - - - - - - - - -
FINANZAS - 0 - 0 -
- - - - - - - - - - - -
- -
0
-
- -
0
-
- -
0
-
-
+ +
+

PROYECTOS TERMINADOS (T)

+
+ +
- -
- - - - - - - - - - - - -
INVESTIGACIÓN Y DESARROLLO - 0 - 0 -
- - - - - - - - - - - -
- -
0
-
- -
0
-
- -
0
-
-
+ +
+

PROYECTOS ACTIVOS

+
+ +
- -
- - - - - - - - - - - - -
LOGÍSTICA Y SISTEMAS - 0 - 0 -
- - - - - - - - - - - -
- -
0
-
- -
0
-
- -
0
-
-
-
- - -
- - - - - - - - - - - - -
OPERACIONES - 0 - 0 -
- - - - - - - - - - - -
- -
0
-
- -
0
-
- -
0
-
-
-
- - -
- - - - - - - - - - - - -
RECURSOS HUMANOS - 0 - 0 -
- - - - - - - - - - - -
- -
0
-
- -
0
-
- -
0
-
-
-
- - -
- -
- -
-

PROYECTOS TERMINADOS (T)

-
- -
-
- - -
-

PROYECTOS ACTIVOS

-
- -
-
+
+
@@ -995,19 +1028,17 @@ }); $('#resumen-proyectos').html(` - T1T2T3T4 + + T1T2T3T4 + - En Proceso${proyectosEnProceso} - Concretados${conteoProyectos[1].concretados}${conteoProyectos[2].concretados}${conteoProyectos[3].concretados}${conteoProyectos[4].concretados} - - `); - $('#resumen-incidencias').html(` - T1T2T3T4 - - En Proceso${incidenciasEnProceso} - Resueltos${conteoIncidencias[1].resueltos}${conteoIncidencias[2].resueltos}${conteoIncidencias[3].resueltos}${conteoIncidencias[4].resueltos} + Proyectos En Proceso${proyectosEnProceso} + Proyectos Concretados${conteoProyectos[1].concretados}${conteoProyectos[2].concretados}${conteoProyectos[3].concretados}${conteoProyectos[4].concretados} + Incidencias En Proceso${incidenciasEnProceso} + Incidencias Resueltas${conteoIncidencias[1].resueltos}${conteoIncidencias[2].resueltos}${conteoIncidencias[3].resueltos}${conteoIncidencias[4].resueltos} `); + $('#resumen-incidencias').empty(); // --- 3. Llenar columnas de proyectos --- const completedContainer = $('#completed-projects-container'); @@ -1031,11 +1062,232 @@ actualizarTarjetaDepto(deptoId); }); - // --- 5. Inicializar todas las interacciones de Drag & Drop --- - inicializarInteraccionesDragDrop(); + // --- 5. Dibujar gráfica de progresión (barras de objetivos $100k + línea de ingresos) --- + dibujarGraficaProgresion(); - // --- 6. Seleccionar el primer departamento para activar la vista - // La función original en jsCode.js ya se encarga de esto al final de `continuamos()` + // --- 6. Inicializar todas las interacciones de Drag & Drop --- + inicializarInteraccionesDragDrop(); + } + + $(window).on('resize', function() { + dibujarGraficaProgresion(); + }); + + $(document).on('mouseenter', '#grafico-progresion-container', function() { + const popover = $('#grafico-progresion-popover'); + + // Posicionamiento dinámico para evitar salirse por arriba de la pantalla + const containerOffset = $(this).offset(); + const scrollTop = $(window).scrollTop(); + const desiredTop = -90; // Posición más baja centrada sobre el bloque 3 + + if (containerOffset.top + desiredTop < scrollTop + 10) { + const safeTop = Math.max(-20, (scrollTop + 10) - containerOffset.top); + popover.css('top', safeTop + 'px'); + } else { + popover.css('top', desiredTop + 'px'); + } + + popover.stop(true, true).fadeIn(150); + dibujarGraficaProgresionCanvas('grafico-progresion-canvas-popover', true); + }); + + $(document).on('mouseleave', '#grafico-progresion-container', function() { + $('#grafico-progresion-popover').stop(true, true).fadeOut(150); + }); + + function dibujarGraficaProgresion() { + dibujarGraficaProgresionCanvas('grafico-progresion-canvas', false); + } + + function dibujarGraficaProgresionCanvas(canvasId, isExpanded) { + const canvas = document.getElementById(canvasId); + if (!canvas) return; + + const ctx = canvas.getContext('2d'); + const rect = canvas.getBoundingClientRect(); + const width = rect.width || canvas.clientWidth || (isExpanded ? 560 : 220); + const height = isExpanded ? 185 : 75; + + const dpr = window.devicePixelRatio || 1; + canvas.width = width * dpr; + canvas.height = height * dpr; + ctx.scale(dpr, dpr); + + ctx.clearRect(0, 0, width, height); + + // Datos guardados de Objetivos e Ingresos de la sesión + const objetivos = [ + parseInt(arregloGeneral.objetivoT1) || 100000, + parseInt(arregloGeneral.objetivoT2) || 100000, + parseInt(arregloGeneral.objetivoT3) || 100000, + parseInt(arregloGeneral.objetivoT4) || 100000 + ]; + const ingresos = [ + arregloGeneral.trimestre1 || 0, + arregloGeneral.trimestre2 || 0, + arregloGeneral.trimestre3 || 0, + arregloGeneral.trimestre4 || 0 + ]; + + const maxVal = Math.max(120000, ...ingresos, ...objetivos) * 1.18; + + const marginTop = isExpanded ? 28 : 14; + const marginBottom = isExpanded ? 24 : 14; + const marginLeft = isExpanded ? 55 : 32; + const marginRight = isExpanded ? 15 : 8; + const chartWidth = width - marginLeft - marginRight; + const chartHeight = height - marginTop - marginBottom; + + // 1. Líneas de cuadrícula horizontales + ctx.strokeStyle = '#e6e6e6'; + ctx.lineWidth = 1; + ctx.fillStyle = '#666'; + ctx.font = isExpanded ? '12px Arial' : '9px Arial'; + ctx.textAlign = 'right'; + ctx.textBaseline = 'middle'; + + const steps = isExpanded ? 3 : 2; + for (let i = 0; i <= steps; i++) { + const val = (maxVal / steps) * i; + const y = height - marginBottom - (chartHeight / steps) * i; + + ctx.beginPath(); + ctx.moveTo(marginLeft, y); + ctx.lineTo(width - marginRight, y); + ctx.stroke(); + + let label = '$' + (val >= 1000 ? Math.round(val / 1000) + 'k' : Math.round(val)); + ctx.fillText(label, marginLeft - (isExpanded ? 6 : 3), y); + } + + const colStep = chartWidth / 4; + const barWidth = Math.min(isExpanded ? 48 : 22, colStep * 0.45); + + // 2. Dibujar Barras (Objetivos del Trimestre guardados) + for (let i = 0; i < 4; i++) { + const xCenter = marginLeft + colStep * i + colStep / 2; + const xBar = xCenter - barWidth / 2; + const barH = (objetivos[i] / maxVal) * chartHeight; + const yBar = height - marginBottom - barH; + + ctx.fillStyle = '#5c9ecc'; + ctx.fillRect(xBar, yBar, barWidth, barH); + ctx.strokeStyle = '#3b7bb2'; + ctx.lineWidth = 1.2; + ctx.strokeRect(xBar, yBar, barWidth, barH); + + ctx.fillStyle = '#333'; + ctx.font = isExpanded ? 'bold 12px Arial' : 'bold 9px Arial'; + ctx.textAlign = 'center'; + ctx.textBaseline = 'top'; + ctx.fillText('T' + (i + 1), xCenter, height - marginBottom + (isExpanded ? 5 : 3)); + + if (isExpanded) { + ctx.fillStyle = '#1b4f72'; + ctx.font = '11px Arial'; + let formattedObj = '$' + Math.round(objetivos[i] / 1000) + 'k'; + ctx.fillText('Obj: ' + formattedObj, xCenter, yBar - 14); + } + } + + // 3. Dibujar Línea + Puntos (Ingresos del Trimestre) + const linePoints = []; + for (let i = 0; i < 4; i++) { + const xCenter = marginLeft + colStep * i + colStep / 2; + const valY = (ingresos[i] / maxVal) * chartHeight; + const yLine = height - marginBottom - valY; + linePoints.push({ + x: xCenter, + y: yLine, + val: ingresos[i] + }); + } + + // Trazar línea conectora + ctx.strokeStyle = '#d9534f'; + ctx.lineWidth = isExpanded ? 3.5 : 2; + ctx.beginPath(); + linePoints.forEach((pt, idx) => { + if (idx === 0) ctx.moveTo(pt.x, pt.y); + else ctx.lineTo(pt.x, pt.y); + }); + ctx.stroke(); + + // Puntos y valores + linePoints.forEach((pt) => { + ctx.fillStyle = '#d9534f'; + ctx.beginPath(); + ctx.arc(pt.x, pt.y, isExpanded ? 6 : 3.5, 0, Math.PI * 2); + ctx.fill(); + ctx.strokeStyle = '#ffffff'; + ctx.lineWidth = isExpanded ? 2.5 : 1.5; + ctx.stroke(); + + if (pt.val > 0) { + ctx.fillStyle = '#a94442'; + ctx.font = isExpanded ? 'bold 12px Arial' : 'bold 8px Arial'; + ctx.textAlign = 'center'; + ctx.textBaseline = 'bottom'; + let formattedVal = isExpanded ? formatCurrency(pt.val) : ('$' + (pt.val >= 1000 ? Math.round(pt.val / 1000) + 'k' : pt.val)); + ctx.fillText(formattedVal, pt.x, pt.y - (isExpanded ? 7 : 3)); + } + }); + + // 4. Leyenda en la esquina superior derecha + ctx.font = isExpanded ? '11px Arial' : '8px Arial'; + ctx.textAlign = 'left'; + ctx.textBaseline = 'top'; + + const legY = isExpanded ? 4 : 1; + + if (isExpanded) { + // Leyenda amplia (2.5X) + const item1SquareX = width - 230; + const item1TextX = width - 213; + const item2CircleX = width - 95; + const item2TextX = width - 85; + + // 🟦 Cuadro azul + Texto Objetivo + ctx.fillStyle = '#5c9ecc'; + ctx.fillRect(item1SquareX, legY, 12, 12); + ctx.strokeStyle = '#3b7bb2'; + ctx.lineWidth = 1; + ctx.strokeRect(item1SquareX, legY, 12, 12); + ctx.fillStyle = '#444'; + ctx.fillText('Objetivo Trimestre', item1TextX, legY); + + // 🔴 Círculo rojo + Texto Ingreso + ctx.fillStyle = '#d9534f'; + ctx.beginPath(); + ctx.arc(item2CircleX, legY + 6, 4.5, 0, Math.PI * 2); + ctx.fill(); + ctx.strokeStyle = '#ffffff'; + ctx.lineWidth = 1; + ctx.stroke(); + ctx.fillStyle = '#444'; + ctx.fillText('Ingreso Real', item2TextX, legY); + } else { + // Leyenda compacta + const item1SquareX = width - 118; + const item1TextX = width - 108; + const item2CircleX = width - 42; + const item2TextX = width - 35; + + // 🟦 Cuadro azul + Texto Obj. + ctx.fillStyle = '#5c9ecc'; + ctx.fillRect(item1SquareX, legY, 7, 7); + ctx.fillStyle = '#444'; + ctx.fillText('Obj. ($100k)', item1TextX, legY); + + // 🔴 Círculo rojo + Texto Ingreso + ctx.fillStyle = '#d9534f'; + ctx.beginPath(); + ctx.arc(item2CircleX, legY + 3.5, 3, 0, Math.PI * 2); + ctx.fill(); + ctx.fillStyle = '#444'; + ctx.fillText('Ingreso', item2TextX, legY); + } } function crearTarjetaProyecto(proyecto, esCompletado) { @@ -1071,11 +1323,11 @@ const quincena = parseInt($('#etQuincena').val()) || 1; const trimestreActual = Math.floor((quincena - 1) / 6) + 1; const esFinTrimestre = (quincena % 6 === 0); - const yaResolvioManto = (proyecto.mantenimiento !== 'N/A' && proyecto.mantenimientoPagadoEnTrimestre === trimestreActual); + const yaResolvioManto = (proyecto.mantenimientoPagadoEnTrimestre === trimestreActual); let actionButtonsHtml = ''; - if (!esCompletado && esFinTrimestre && proyecto.dificultad !== 'Incidencia' && proyecto.mantenimiento !== 'N/A') { - const showPay = proyecto.mantenimiento !== 'N/A'; + if (!esCompletado && esFinTrimestre) { + const showPay = (proyecto.dificultad !== 'Incidencia' && proyecto.mantenimiento !== 'N/A'); const penalizacionFormateada = formatCurrency(proyecto.penalizacion || 0); let buttonManto = '';