mirror of
https://github.com/cheveguerra/Colaborando.git
synced 2026-08-19 00:46:37 +00:00
* **Nuevo botón de Pantalla Completa:** Integrado en los encabezados de la pantalla de Resumen y el Tablero mediante la API de Fullscreen de HTML5, actualizando su texto de forma reactiva a eventos del navegador.
* **Alineación de Departamentos:** Se fijó la altura (38px) de la celda de habilidad especial en el tablero y se restauró el tamaño de letra original (14px) con un line-height controlado (1.2) para evitar desfases de altura cuando el texto ocupa dos líneas sin perder legibilidad.
This commit is contained in:
+354
-14
@@ -256,7 +256,10 @@
|
||||
<img id="logo" src="img/logoColaborando-tablero.png">
|
||||
<div style="display: flex; justify-content: space-between; align-items: center; padding: 5px 10px;">
|
||||
<div id="nombreSesion" style="font-weight: bold; font-size: 16px; color: #555;"></div>
|
||||
<a href="colaborando2.php?n=<?php echo htmlspecialchars($_GET['n'] ?? $_GET['nombre'] ?? ''); ?>" class="nav-button">Ir al Tablero</a>
|
||||
<div style="display: flex; gap: 5px;">
|
||||
<button id="btnFullscreen" onclick="toggleFullscreen()" class="nav-button" style="border: none; cursor: pointer;">Pantalla Completa</button>
|
||||
<a href="colaborando2.php?n=<?php echo htmlspecialchars($_GET['n'] ?? $_GET['nombre'] ?? ''); ?>" class="nav-button">Ir al Tablero</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card">
|
||||
@@ -778,10 +781,14 @@
|
||||
|
||||
if (!deptoArr || !deptoCard.length) return;
|
||||
|
||||
// Actualizar recursos
|
||||
deptoCard.find('#tiempoCh').text(findArrayValue(deptoArr, 'recursoTiempo'));
|
||||
deptoCard.find('#personalCh').text(findArrayValue(deptoArr, 'recursoPersonal'));
|
||||
deptoCard.find('#capitalCh').text(findArrayValue(deptoArr, 'recursoCapital'));
|
||||
// Actualizar recursos (con selector flexible para soportar el deptoActual con IDs renombrados)
|
||||
const tiempoElem = deptoCard.find('#tiempoCh').length ? deptoCard.find('#tiempoCh') : deptoCard.find('#recursoDispTiempoGde');
|
||||
const personalElem = deptoCard.find('#personalCh').length ? deptoCard.find('#personalCh') : deptoCard.find('#recursoDispPersonalGde');
|
||||
const capitalElem = deptoCard.find('#capitalCh').length ? deptoCard.find('#capitalCh') : deptoCard.find('#recursoDispCapitalGde');
|
||||
|
||||
tiempoElem.text(findArrayValue(deptoArr, 'recursoTiempo'));
|
||||
personalElem.text(findArrayValue(deptoArr, 'recursoPersonal'));
|
||||
capitalElem.text(findArrayValue(deptoArr, 'recursoCapital'));
|
||||
|
||||
// Actualizar activos
|
||||
deptoCard.find('#activoCh1').text(findArrayValue(deptoArr, 'activo1'));
|
||||
@@ -1014,7 +1021,11 @@
|
||||
if (p.estatus === 'Completado' && p.trimestreCompletado === trimestreActual) {
|
||||
completedContainer.append(crearTarjetaProyecto(p, true));
|
||||
} else if (p.estatus === 'En proceso') {
|
||||
activeContainer.append(crearTarjetaProyecto(p, false));
|
||||
// Solo se muestra en el resumen si no ha pagado mantenimiento en este trimestre
|
||||
console.log("[ResumenUI] Proyecto:", p.nombreProyecto, "estatus:", p.estatus, "mantoPagadoEnTrimestre:", p.mantenimientoPagadoEnTrimestre, "trimestreActual:", trimestreActual);
|
||||
if (p.mantenimientoPagadoEnTrimestre !== trimestreActual) {
|
||||
activeContainer.append(crearTarjetaProyecto(p, false));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1061,15 +1072,53 @@
|
||||
|
||||
const completadoClass = esCompletado ? 'completado' : '';
|
||||
|
||||
return `
|
||||
<div class="project-card ${completadoClass}" style="flex-basis: 48%;">
|
||||
<div class="project-header">${proyecto.nombreProyecto.toUpperCase()}</div>
|
||||
<div class="project-body">
|
||||
<i>${proyecto.descripcion}</i>
|
||||
<div class="project-assets-req">${activosReqHtml}</div>
|
||||
<div class="project-assets-asig">${activosAsigHtml}</div>
|
||||
const quincena = parseInt($('#etQuincena').val()) || 1;
|
||||
const esFinTrimestre = (quincena % 6 === 0);
|
||||
|
||||
let actionButtonsHtml = '';
|
||||
if (!esCompletado && esFinTrimestre) {
|
||||
const showPay = proyecto.mantenimiento !== 'N/A';
|
||||
const buttonManto = showPay ?
|
||||
`<button class="nav-button btn-pagar-mantenimiento" data-id="${proyecto.id}" style="background-color: #28a745; border: none; font-size: 11px; padding: 5px 8px; cursor: pointer; flex: 1;">Pagar Manto</button>` : '';
|
||||
|
||||
const penalizacionFormateada = formatCurrency(proyecto.penalizacion || 0);
|
||||
const buttonDesechar = `<button class="nav-button btn-desechar-proyecto" data-id="${proyecto.id}" style="background-color: #dc3545; border: none; font-size: 11px; padding: 5px 8px; cursor: pointer; flex: 1;">Desechar (${penalizacionFormateada})</button>`;
|
||||
|
||||
actionButtonsHtml = `
|
||||
<div class="project-actions" style="display: flex; gap: 8px; margin-top: 10px; width: 100%;">
|
||||
${buttonManto}
|
||||
${buttonDesechar}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
let infoAdicionalHtml = '';
|
||||
if (!esCompletado) {
|
||||
const penalizacionFormateada = formatCurrency(proyecto.penalizacion || 0);
|
||||
const potencialVal = proyecto.potencial || 'N/A';
|
||||
infoAdicionalHtml = `
|
||||
<div class="project-info-adicional" style="font-size: 1.2em; color: #444; margin-top: 8px; border-top: 1px dashed #ccc; padding-top: 5px; text-align: left; display: flex; justify-content: space-between;">
|
||||
<span><b>Potencial:</b> ${potencialVal}</span>
|
||||
<span><b>Penalización:</b> ${penalizacionFormateada}</span>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
return `
|
||||
<div class="project-card ${completadoClass}" style="flex-basis: 48%; display: flex; flex-direction: column; justify-content: space-between; min-height: 180px; box-sizing: border-box; padding: 10px;">
|
||||
<div>
|
||||
<div class="project-header" style="font-weight: bold; margin-bottom: 5px; text-align: center; border-bottom: 1px solid gray; padding-bottom: 4px;">${proyecto.nombreProyecto.toUpperCase()}</div>
|
||||
<div class="project-body" style="padding: 5px; text-align: center;">
|
||||
<i style="font-size: 0.9em; color: #555;">${proyecto.descripcion}</i>
|
||||
<div class="project-assets-req" style="margin-top: 5px; display: flex; justify-content: center; gap: 2px;">${activosReqHtml}</div>
|
||||
<div class="project-assets-asig" style="margin-top: 5px; display: flex; justify-content: center; gap: 2px;">${activosAsigHtml}</div>
|
||||
${infoAdicionalHtml}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="project-footer" style="font-size: 0.9em; margin-top: 8px; font-weight: bold; border-top: 1px solid #ddd; padding-top: 5px; text-align: center;">MANTENIMIENTO: ${proyecto.mantenimiento}</div>
|
||||
${actionButtonsHtml}
|
||||
</div>
|
||||
<div class="project-footer">MANTENIMIENTO: ${proyecto.mantenimiento}</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
@@ -1447,6 +1496,297 @@
|
||||
}
|
||||
});
|
||||
|
||||
function parseMantenimiento(mantenimientoStr) {
|
||||
let t = 0,
|
||||
p = 0,
|
||||
c = 0;
|
||||
if (mantenimientoStr && mantenimientoStr !== 'N/A') {
|
||||
const matchesT = mantenimientoStr.match(/(\d+)T/i);
|
||||
const matchesP = mantenimientoStr.match(/(\d+)P/i);
|
||||
const matchesC = mantenimientoStr.match(/(\d+)C/i);
|
||||
if (matchesT) t = parseInt(matchesT[1]);
|
||||
if (matchesP) p = parseInt(matchesP[1]);
|
||||
if (matchesC) c = parseInt(matchesC[1]);
|
||||
}
|
||||
return {
|
||||
t,
|
||||
p,
|
||||
c
|
||||
};
|
||||
}
|
||||
|
||||
function pagarMantenimientoProyecto(projectId) {
|
||||
console.log("[PagarManto] Iniciando pago para proyecto ID:", projectId);
|
||||
if (typeof deptoActual === 'undefined' || !deptoActual) {
|
||||
alert('Por favor, selecciona primero un departamento haciendo clic en él.');
|
||||
return;
|
||||
}
|
||||
|
||||
const proyecto = proyectos.find(p => p.id == projectId);
|
||||
if (!proyecto) {
|
||||
alert('No se encontró el proyecto.');
|
||||
console.error("[PagarManto] Proyecto no encontrado con ID:", projectId);
|
||||
return;
|
||||
}
|
||||
console.log("[PagarManto] Proyecto encontrado:", proyecto.nombreProyecto);
|
||||
|
||||
// Parse maintenance cost
|
||||
const cost = parseMantenimiento(proyecto.mantenimiento);
|
||||
console.log("[PagarManto] Costo parsed:", cost);
|
||||
|
||||
// Get department resources
|
||||
const deptoArr = arreglosDeptos['arr' + deptoActual];
|
||||
const recT = parseInt(findArrayValue(deptoArr, 'recursoTiempo')) || 0;
|
||||
const recP = parseInt(findArrayValue(deptoArr, 'recursoPersonal')) || 0;
|
||||
const recC = parseInt(findArrayValue(deptoArr, 'recursoCapital')) || 0;
|
||||
console.log("[PagarManto] Recursos depto actual:", {
|
||||
recT,
|
||||
recP,
|
||||
recC
|
||||
});
|
||||
|
||||
// Verificar si LogisticaYSistemas tiene comprada y disponible su habilidad especial
|
||||
let logisticaHabilidadEspecialDisponible = false;
|
||||
let elArregloLogistica = null;
|
||||
const quincena = parseInt($('#etQuincena').val()) || 1;
|
||||
const trimestreActual = Math.floor((quincena - 1) / 6) + 1;
|
||||
|
||||
if (typeof deptoActual !== 'undefined' && deptoActual === 'LogisticaYSistemas') {
|
||||
elArregloLogistica = arreglosDeptos['arrLogisticaYSistemas'];
|
||||
if (elArregloLogistica) {
|
||||
const accionQuincenal = findArrayValue(elArregloLogistica, 'accionEspecialQuincenal');
|
||||
if (accionQuincenal && accionQuincenal !== '') {
|
||||
const lastUsed = parseInt(findArrayValue(elArregloLogistica, 'accionEspecialLastUsed')) || 0;
|
||||
const periodicidad = parseInt(findArrayValue(elArregloLogistica, 'accionEspecialPeriodicidad')) || 2;
|
||||
if (lastUsed === 0 || quincena >= lastUsed + periodicidad) {
|
||||
logisticaHabilidadEspecialDisponible = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const deptoNombre = findArrayValue(deptoArr, 'nombre');
|
||||
|
||||
if (logisticaHabilidadEspecialDisponible) {
|
||||
const dialogMsg = `El departamento de Logística y Sistemas tiene disponible la Habilidad Especial para omitir un mantenimiento sin costo.<br><br>` +
|
||||
`¿Deseas usar la Habilidad Especial para omitir el mantenimiento del proyecto "${proyecto.nombreProyecto}" <b>sin gastar recursos</b>,<br>` +
|
||||
`o prefieres realizar un pago normal gastando recursos (${proyecto.mantenimiento})?`;
|
||||
|
||||
$('<div></div>')
|
||||
.html(dialogMsg)
|
||||
.dialog({
|
||||
modal: true,
|
||||
title: 'Pagar Mantenimiento - Logística y Sistemas',
|
||||
width: 480,
|
||||
buttons: {
|
||||
"Usar Habilidad (Sin costo)": function() {
|
||||
$(this).dialog('close');
|
||||
// Mark project maintenance as paid in the current trimester
|
||||
proyecto.mantenimientoPagadoEnTrimestre = trimestreActual;
|
||||
|
||||
// Update skill cooldown
|
||||
setArrayValue(elArregloLogistica, 'accionEspecialLastUsed', quincena);
|
||||
|
||||
// Save and update UI
|
||||
actualizarResumenUI();
|
||||
actualizarTarjetaDepto('LogisticaYSistemas'); // Refresca la tarjeta en la UI
|
||||
|
||||
var audio = new Audio('./Microwave_bell.wav');
|
||||
audio.play();
|
||||
|
||||
alert(`Se ha omitido el mantenimiento del proyecto "${proyecto.nombreProyecto}" sin costo utilizando la Habilidad Especial de Logística y Sistemas.`);
|
||||
|
||||
if (typeof guardarDatosEnServidor === 'function') {
|
||||
guardarDatosEnServidor();
|
||||
}
|
||||
},
|
||||
"Pagar Normal (Con costo)": function() {
|
||||
$(this).dialog('close');
|
||||
ejecutarPagoNormal(proyecto, cost, deptoArr, recT, recP, recC, deptoNombre, trimestreActual, quincena);
|
||||
},
|
||||
"Cancelar": function() {
|
||||
$(this).dialog('close');
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
ejecutarPagoNormal(proyecto, cost, deptoArr, recT, recP, recC, deptoNombre, trimestreActual, quincena);
|
||||
}
|
||||
}
|
||||
|
||||
function ejecutarPagoNormal(proyecto, cost, deptoArr, recT, recP, recC, deptoNombre, trimestreActual, quincena) {
|
||||
if (recT < cost.t || recP < cost.p || recC < cost.c) {
|
||||
alert(`El departamento seleccionado (${deptoNombre}) NO tiene suficientes recursos para pagar el mantenimiento del proyecto "${proyecto.nombreProyecto}".\n` +
|
||||
`Costo requerido: Tiempo: ${cost.t}, Personal: ${cost.p}, Capital: ${cost.c}\n` +
|
||||
`Recursos actuales: Tiempo: ${recT}, Personal: ${recP}, Capital: ${recC}`);
|
||||
return;
|
||||
}
|
||||
|
||||
const confirmMessage = `¿Estás seguro de que deseas pagar el mantenimiento de <b>${proyecto.mantenimiento}</b> para el proyecto "${proyecto.nombreProyecto}" desde el departamento <b>${deptoNombre}</b>?`;
|
||||
|
||||
confirm(confirmMessage, function(confirmed) {
|
||||
if (confirmed) {
|
||||
// Deduct resources
|
||||
setArrayValue(deptoArr, 'recursoTiempo', recT - cost.t);
|
||||
setArrayValue(deptoArr, 'recursoPersonal', recP - cost.p);
|
||||
setArrayValue(deptoArr, 'recursoCapital', recC - cost.c);
|
||||
|
||||
// Mark project maintenance as paid in the current trimester
|
||||
proyecto.mantenimientoPagadoEnTrimestre = trimestreActual;
|
||||
console.log("[PagarManto] Asignado mantenimientoPagadoEnTrimestre a:", trimestreActual);
|
||||
|
||||
// Save and update UI
|
||||
actualizarTarjetaDepto(deptoActual);
|
||||
console.log("[PagarManto] Tarjeta departamento actualizada.");
|
||||
|
||||
actualizarResumenUI();
|
||||
console.log("[PagarManto] UI de resumen actualizada.");
|
||||
|
||||
var audio = new Audio('./Microwave_bell.wav');
|
||||
audio.play();
|
||||
|
||||
alert(`Mantenimiento pagado con éxito para el proyecto "${proyecto.nombreProyecto}" desde el departamento ${deptoNombre}. El proyecto continuará en proceso en el siguiente trimestre.`);
|
||||
|
||||
if (typeof guardarDatosEnServidor === 'function') {
|
||||
console.log("[PagarManto] Guardando sesión en el servidor...");
|
||||
guardarDatosEnServidor();
|
||||
}
|
||||
}
|
||||
}, 450);
|
||||
}
|
||||
|
||||
function desecharProyecto(projectId) {
|
||||
const proyecto = proyectos.find(p => p.id == projectId);
|
||||
if (!proyecto) {
|
||||
alert('No se encontró el proyecto.');
|
||||
return;
|
||||
}
|
||||
|
||||
const penalizacionVal = parseInt(proyecto.penalizacion) || 0;
|
||||
const penalizacionFormateada = formatCurrency(penalizacionVal);
|
||||
|
||||
const quincena = parseInt($('#etQuincena').val()) || 1;
|
||||
const trimestreActual = Math.floor((quincena - 1) / 6) + 1;
|
||||
|
||||
// Verificar si Operaciones tiene comprada y disponible su habilidad especial
|
||||
let operacionesHabilidadEspecialDisponible = false;
|
||||
let elArregloOperaciones = null;
|
||||
if (typeof deptoActual !== 'undefined' && deptoActual === 'Operaciones') {
|
||||
elArregloOperaciones = arreglosDeptos['arrOperaciones'];
|
||||
if (elArregloOperaciones) {
|
||||
const accionQuincenal = findArrayValue(elArregloOperaciones, 'accionEspecialQuincenal');
|
||||
if (accionQuincenal && accionQuincenal !== '') {
|
||||
const lastUsed = parseInt(findArrayValue(elArregloOperaciones, 'accionEspecialLastUsed')) || 0;
|
||||
const periodicidad = parseInt(findArrayValue(elArregloOperaciones, 'accionEspecialPeriodicidad')) || 2;
|
||||
if (lastUsed === 0 || quincena >= lastUsed + periodicidad) {
|
||||
operacionesHabilidadEspecialDisponible = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (operacionesHabilidadEspecialDisponible) {
|
||||
const dialogMsg = `El departamento de Operaciones tiene disponible la Habilidad Especial para descartar un proyecto sin costo.<br><br>` +
|
||||
`¿Deseas usar la Habilidad Especial para desechar el proyecto "${proyecto.nombreProyecto}" <b>sin pagar la penalización</b>,<br>` +
|
||||
`o prefieres realizar un desecho normal pagando la penalización de <b>${penalizacionFormateada}</b>?`;
|
||||
|
||||
$('<div></div>')
|
||||
.html(dialogMsg)
|
||||
.dialog({
|
||||
modal: true,
|
||||
title: 'Desechar Proyecto - Operaciones',
|
||||
width: 480,
|
||||
buttons: {
|
||||
"Usar Habilidad (Sin costo)": function() {
|
||||
$(this).dialog('close');
|
||||
// Mark project as discarded
|
||||
proyecto.estatus = 'descartado';
|
||||
|
||||
// Update skill cooldown
|
||||
setArrayValue(elArregloOperaciones, 'accionEspecialLastUsed', quincena);
|
||||
|
||||
// Save and update UI
|
||||
actualizarResumenUI();
|
||||
actualizarTarjetaDepto('Operaciones'); // Refresca la tarjeta del depto en la UI
|
||||
|
||||
var audio = new Audio('./Microwave_bell.wav');
|
||||
audio.play();
|
||||
|
||||
alert(`El proyecto "${proyecto.nombreProyecto}" ha sido desechado sin costo utilizando la Habilidad Especial de Operaciones.`);
|
||||
|
||||
if (typeof guardarDatosEnServidor === 'function') {
|
||||
guardarDatosEnServidor();
|
||||
}
|
||||
},
|
||||
"Desechar Normal (Con costo)": function() {
|
||||
$(this).dialog('close');
|
||||
ejecutarDesechoNormal(proyecto, penalizacionVal, penalizacionFormateada, trimestreActual);
|
||||
},
|
||||
"Cancelar": function() {
|
||||
$(this).dialog('close');
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
ejecutarDesechoNormal(proyecto, penalizacionVal, penalizacionFormateada, trimestreActual);
|
||||
}
|
||||
}
|
||||
|
||||
function ejecutarDesechoNormal(proyecto, penalizacionVal, penalizacionFormateada, trimestreActual) {
|
||||
// Validar que el acumulado general acumulado hasta el trimestre actual sea suficiente
|
||||
let acumuladoDisponible = 0;
|
||||
for (let t = 1; t <= trimestreActual; t++) {
|
||||
acumuladoDisponible += (arregloGeneral['trimestre' + t] || 0);
|
||||
}
|
||||
|
||||
if (acumuladoDisponible < penalizacionVal) {
|
||||
alert(`No puedes desechar el proyecto "${proyecto.nombreProyecto}" porque no hay acumulado general suficiente.\n` +
|
||||
`Penalización requerida: ${penalizacionFormateada}\n` +
|
||||
`Acumulado general disponible: ${formatCurrency(acumuladoDisponible)}`);
|
||||
return;
|
||||
}
|
||||
|
||||
const confirmMessage = `¿Estás seguro de que deseas desechar el proyecto "${proyecto.nombreProyecto}"?<br>` +
|
||||
`Se aplicará una penalización de <b>${penalizacionFormateada}</b> del acumulado general.`;
|
||||
|
||||
confirm(confirmMessage, function(confirmed) {
|
||||
if (confirmed) {
|
||||
// Subtract from current trimester's earnings
|
||||
const trimestreKey = 'trimestre' + trimestreActual;
|
||||
if (typeof arregloGeneral[trimestreKey] === 'undefined') {
|
||||
arregloGeneral[trimestreKey] = 0;
|
||||
}
|
||||
arregloGeneral[trimestreKey] -= penalizacionVal;
|
||||
|
||||
// Mark project as discarded
|
||||
proyecto.estatus = 'descartado';
|
||||
|
||||
// Save and update UI
|
||||
actualizarResumenUI();
|
||||
|
||||
var audio = new Audio('./Microwave_bell.wav');
|
||||
audio.play();
|
||||
|
||||
alert(`El proyecto "${proyecto.nombreProyecto}" ha sido desechado. Se aplicó una penalización de ${penalizacionFormateada}.`);
|
||||
|
||||
if (typeof guardarDatosEnServidor === 'function') {
|
||||
guardarDatosEnServidor();
|
||||
}
|
||||
}
|
||||
}, 450);
|
||||
}
|
||||
|
||||
// Delegación de eventos para los botones de pagar y desechar
|
||||
$(document).on('click', '.btn-pagar-mantenimiento', function() {
|
||||
const id = $(this).data('id');
|
||||
pagarMantenimientoProyecto(id);
|
||||
});
|
||||
|
||||
$(document).on('click', '.btn-desechar-proyecto', function() {
|
||||
const id = $(this).data('id');
|
||||
desecharProyecto(id);
|
||||
});
|
||||
|
||||
// El listener para el click en .departamentos ya está en jsCode.js y funcionará ahora
|
||||
</script>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user