feat: cierre anticipado de tomas prolongadas con justificación
- DB: añade estado 'cerrado_anticipado' al enum de turnero_consentimientos - ver_formulario_enviado.php: POST handler mp_cerrar_anticipado guarda motivo en _cierre_anticipado y cambia estado; botón "Cerrar anticipadamente" con modal textarea; bloque display para estado cerrado_anticipado mostrando el motivo; modoEditar y check de estado actualizados para excluir cerrado_anticipado - get_consentimientos.php: expone cierre_anticipado en respuesta; mueve parse de datos_respuestas fuera del if(esTomaProg) para ambos loops - lugar.php: CONSENT_IC/LBL incluye cerrado_anticipado; ya[] lo incluye; hayPendientes lo excluye; fila muestra motivo en banner amarillo - recepcion.php: CONSENT_META incluye cerrado_anticipado; ya[] lo incluye; motivo visible en la tarjeta del consentimiento Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
915fa3ba5c
commit
c9ea76f782
@@ -81,10 +81,10 @@ foreach ($consentimientos as &$c) {
|
||||
// Toma progresiva: override explícito en DB gana; si es NULL, usar heurística (>1 campo firma_profesional)
|
||||
$overrideProg = $c['formulario_es_toma_prog'] ?? null;
|
||||
$esTomaProg = $overrideProg !== null ? (bool)(int)$overrideProg : count($camposFirmaPro) > 1;
|
||||
$dr = $c['datos_respuestas'] ? (json_decode($c['datos_respuestas'], true) ?? []) : [];
|
||||
$tomasTotal = 0;
|
||||
$tomasFirm = 0;
|
||||
if ($esTomaProg) {
|
||||
$dr = $c['datos_respuestas'] ? (json_decode($c['datos_respuestas'], true) ?? []) : [];
|
||||
|
||||
// Mapear cada firma_profesional a los valores de examen de su sección condicional
|
||||
$firmaCondMap = [];
|
||||
@@ -147,6 +147,7 @@ foreach ($consentimientos as &$c) {
|
||||
$c['es_toma_progresiva'] = $esTomaProg;
|
||||
$c['tomas_total'] = $tomasTotal;
|
||||
$c['tomas_firmadas'] = $tomasFirm;
|
||||
$c['cierre_anticipado'] = $dr['_cierre_anticipado'] ?? null;
|
||||
// Exponer campos firma_profesional (ids) para el frontend
|
||||
$c['campos_firma_pro'] = array_column($camposFirmaPro, 'id');
|
||||
unset($c['formulario_esquema'], $c['formulario_es_toma_prog'], $c['formulario_solo_profesional'], $c['datos_respuestas']);
|
||||
@@ -333,10 +334,10 @@ if ($incluirSolicitud) {
|
||||
$tieneFirmaPac = !empty(array_filter($campos, fn($f) => ($f['tipo'] ?? '') === 'firma'));
|
||||
$overrideProg = $c['formulario_es_toma_prog'] ?? null;
|
||||
$esTomaProg = $overrideProg !== null ? (bool)(int)$overrideProg : count($camposFirmaPro) > 1;
|
||||
$dr2 = $c['datos_respuestas'] ? (json_decode($c['datos_respuestas'], true) ?? []) : [];
|
||||
$tomasTotal2 = 0;
|
||||
$tomasFirm = 0;
|
||||
if ($esTomaProg) {
|
||||
$dr2 = $c['datos_respuestas'] ? (json_decode($c['datos_respuestas'], true) ?? []) : [];
|
||||
$firmaCondMap2 = [];
|
||||
$curC2 = null; $curV2 = [];
|
||||
foreach ($campos as $campo2) {
|
||||
@@ -384,6 +385,7 @@ if ($incluirSolicitud) {
|
||||
$c['es_toma_progresiva'] = $esTomaProg;
|
||||
$c['tomas_total'] = $tomasTotal2;
|
||||
$c['tomas_firmadas'] = $tomasFirm;
|
||||
$c['cierre_anticipado'] = $dr2['_cierre_anticipado'] ?? null;
|
||||
$c['campos_firma_pro'] = array_column($camposFirmaPro, 'id');
|
||||
unset($c['formulario_esquema'], $c['formulario_es_toma_prog'], $c['formulario_solo_profesional'], $c['datos_respuestas']);
|
||||
}
|
||||
|
||||
@@ -386,7 +386,8 @@ foreach ($lugares as $_el) {
|
||||
padding: .5rem .7rem; border-radius: 10px; margin-bottom: .35rem;
|
||||
font-size: .84rem; border: 1px solid transparent; min-height: 46px;
|
||||
}
|
||||
.consent-row.firmado { background: #f0fdf4; color: #166534; border-color: #bbf7d0; }
|
||||
.consent-row.firmado { background: #f0fdf4; color: #166534; border-color: #bbf7d0; }
|
||||
.consent-row.cerrado_anticipado { background: #fef9c3; color: #78350f; border-color: #fde68a; }
|
||||
.consent-row.rechazado { background: #f8fafc; color: #64748b; border-color: #e2e8f0; }
|
||||
.consent-row.enviado { background: #eff6ff; color: #1e40af; border-color: #bfdbfe; }
|
||||
.consent-row.pendiente { background: #fffbeb; color: #92400e; border-color: #fde68a; }
|
||||
@@ -1394,10 +1395,11 @@ const CONSENT_IC = {
|
||||
firmado:'fa-check-circle', rechazado:'fa-ban',
|
||||
enviado:'fa-envelope', visto:'fa-eye', pendiente:'fa-clock',
|
||||
en_progreso:'fa-hourglass-half',
|
||||
cerrado_anticipado:'fa-exclamation-triangle',
|
||||
};
|
||||
const CONSENT_LBL = {
|
||||
firmado:'Firmado', rechazado:'Rechazado', enviado:'Enviado', visto:'Visto', pendiente:'Pendiente',
|
||||
en_progreso:'En progreso',
|
||||
en_progreso:'En progreso', cerrado_anticipado:'Cerrado anticipadamente',
|
||||
};
|
||||
|
||||
// ── Countdown en tarjetas de cola (toma progresiva) ──────────
|
||||
@@ -1530,7 +1532,7 @@ function renderConsentimientos(lista) {
|
||||
// Mostrar consentimientos de examen (origen null) + los de esta estación
|
||||
lista = lista.filter(c => !c.origen_lugar_id || c.origen_lugar_id == lugarId);
|
||||
tieneConsent = lista.length > 0;
|
||||
hayPendientes = lista.some(c => !['firmado','rechazado'].includes(c.estado));
|
||||
hayPendientes = lista.some(c => !['firmado','rechazado','cerrado_anticipado'].includes(c.estado));
|
||||
// en_progreso bloquea finalizar pero no muestra aviso de "pendiente sin acción"
|
||||
const pendCount = lista.filter(c => ['pendiente','enviado','visto'].includes(c.estado)).length;
|
||||
|
||||
@@ -1574,7 +1576,7 @@ function renderConsentimientos(lista) {
|
||||
}
|
||||
|
||||
listEl.innerHTML = lista.map(c => {
|
||||
const ya = ['firmado','rechazado'].includes(c.estado);
|
||||
const ya = ['firmado','rechazado','cerrado_anticipado'].includes(c.estado);
|
||||
const ico = CONSENT_IC[c.estado] || 'fa-clock';
|
||||
const label = CONSENT_LBL[c.estado] || c.estado;
|
||||
const token = escHtml(c.token || '');
|
||||
@@ -1631,13 +1633,20 @@ function renderConsentimientos(lista) {
|
||||
<i class="fas fa-trash"></i>
|
||||
</button>`;
|
||||
|
||||
return `<div class="consent-row ${c.estado}" data-consent-id="${c.id}">
|
||||
<i class="fas ${ico}"></i>
|
||||
<span class="nom-form">${escHtml(c.formulario_nombre || 'Consentimiento')}</span>
|
||||
${progresoBadge}
|
||||
${countdownEl}
|
||||
<span class="c-badge">${label}</span>
|
||||
<div class="acciones-consent">${btnAccion}${btnVer}${btnQuitar}</div>
|
||||
const motivoRow = (c.estado === 'cerrado_anticipado' && c.cierre_anticipado?.motivo)
|
||||
? `<div style="font-size:.75rem;color:#92400e;background:#fef3c7;border-radius:4px;padding:3px 8px;margin-top:3px;width:100%">
|
||||
<i class="fas fa-comment-alt me-1"></i>${escHtml(c.cierre_anticipado.motivo)}
|
||||
</div>` : '';
|
||||
return `<div class="consent-row ${c.estado}" data-consent-id="${c.id}" style="flex-wrap:wrap">
|
||||
<div style="display:flex;align-items:center;gap:.4rem;width:100%">
|
||||
<i class="fas ${ico}"></i>
|
||||
<span class="nom-form">${escHtml(c.formulario_nombre || 'Consentimiento')}</span>
|
||||
${progresoBadge}
|
||||
${countdownEl}
|
||||
<span class="c-badge">${label}</span>
|
||||
<div class="acciones-consent">${btnAccion}${btnVer}${btnQuitar}</div>
|
||||
</div>
|
||||
${motivoRow}
|
||||
</div>`;
|
||||
}).join('');
|
||||
}
|
||||
|
||||
@@ -1960,11 +1960,12 @@ async function guardarSolicitud() {
|
||||
|
||||
// ── Labels / iconos para estados ─────────────────────────────
|
||||
const CONSENT_META = {
|
||||
firmado : { cls:'firmado', ico:'fa-check-circle', lbl:'Firmado' },
|
||||
enviado : { cls:'enviado', ico:'fa-envelope', lbl:'Enviado' },
|
||||
visto : { cls:'visto', ico:'fa-eye', lbl:'Visto' },
|
||||
rechazado : { cls:'rechazado', ico:'fa-ban', lbl:'Rechazado' },
|
||||
pendiente : { cls:'pendiente', ico:'fa-clock', lbl:'Pendiente' },
|
||||
firmado : { cls:'firmado', ico:'fa-check-circle', lbl:'Firmado' },
|
||||
enviado : { cls:'enviado', ico:'fa-envelope', lbl:'Enviado' },
|
||||
visto : { cls:'visto', ico:'fa-eye', lbl:'Visto' },
|
||||
rechazado : { cls:'rechazado', ico:'fa-ban', lbl:'Rechazado' },
|
||||
pendiente : { cls:'pendiente', ico:'fa-clock', lbl:'Pendiente' },
|
||||
cerrado_anticipado : { cls:'rechazado', ico:'fa-exclamation-triangle', lbl:'Cerrado anticipadamente' },
|
||||
};
|
||||
|
||||
function renderConsentimientos(lista) {
|
||||
@@ -1977,7 +1978,7 @@ function renderConsentimientos(lista) {
|
||||
}
|
||||
el.innerHTML = lista.map(c => {
|
||||
const m = CONSENT_META[c.estado] || CONSENT_META.pendiente;
|
||||
const ya = ['firmado','rechazado'].includes(c.estado);
|
||||
const ya = ['firmado','rechazado','cerrado_anticipado'].includes(c.estado);
|
||||
const token = escHtml(c.token || '');
|
||||
const nom = escHtml(c.formulario_nombre || 'Consentimiento');
|
||||
const fId = c.formulario_id;
|
||||
@@ -2037,6 +2038,10 @@ function renderConsentimientos(lista) {
|
||||
}
|
||||
}
|
||||
|
||||
const motivoRec = (c.estado === 'cerrado_anticipado' && c.cierre_anticipado?.motivo)
|
||||
? `<div style="font-size:.75rem;color:#92400e;background:#fef3c7;border-radius:4px;padding:3px 8px;margin-top:3px;width:100%">
|
||||
<i class="fas fa-comment-alt me-1"></i>${escHtml(c.cierre_anticipado.motivo)}
|
||||
</div>` : '';
|
||||
return `<div class="consent-item ${m.cls}" style="flex-wrap:wrap">
|
||||
<div style="display:flex;align-items:center;gap:.5rem;width:100%">
|
||||
<i class="fas ${m.ico}"></i>
|
||||
@@ -2045,6 +2050,7 @@ function renderConsentimientos(lista) {
|
||||
<div class="consent-acciones">${btnFirmar}${btnWa}</div>
|
||||
</div>
|
||||
${profRow}
|
||||
${motivoRec}
|
||||
</div>`;
|
||||
}).join('');
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user