feat(lugar): botón quitar consentimiento con motivo obligatorio y auditoría

This commit is contained in:
Lizandro Guarnizo
2026-07-27 20:56:44 -05:00
parent 828681ffd6
commit 3c3e531e48
2 changed files with 131 additions and 1 deletions
@@ -0,0 +1,53 @@
<?php
/**
* modules/turnero/api/quitar_consentimiento.php
* POST {consent_id, motivo} → elimina un consentimiento con auditoría
*/
require_once __DIR__ . '/_helpers.php';
require_once __DIR__ . '/../../../classes/lab/ActividadAdmin.php';
requireTurnero();
requireMethod('POST');
$input = inputJson();
$consentId = (int)($input['consent_id'] ?? 0);
$motivo = trim($input['motivo'] ?? '');
if (!$consentId) jsonError('consent_id requerido');
if ($motivo === '') jsonError('Debe ingresar un motivo para quitar el consentimiento');
$pdo = db();
// Leer datos antes de borrar (para el log)
$row = $pdo->prepare(
"SELECT tc.id, tc.turno_id, tc.formulario_id, tc.estado,
lf.nombre AS formulario_nombre,
t.codigo AS turno_codigo
FROM turnero_consentimientos tc
JOIN lab_formularios lf ON lf.id = tc.formulario_id
JOIN turnero_turnos t ON t.id = tc.turno_id
WHERE tc.id = ? LIMIT 1"
);
$row->execute([$consentId]);
$consent = $row->fetch(PDO::FETCH_ASSOC);
if (!$consent) jsonError('Consentimiento no encontrado', 404);
$pdo->prepare('DELETE FROM turnero_consentimientos WHERE id = ?')->execute([$consentId]);
(new ActividadAdmin())->registrar(
adminId(),
'turnero_config',
'quitar_consentimiento',
$consentId,
[
'turno_codigo' => $consent['turno_codigo'],
'turno_id' => $consent['turno_id'],
'formulario_id' => $consent['formulario_id'],
'formulario_nombre' => $consent['formulario_nombre'],
'estado_previo' => $consent['estado'],
'motivo' => $motivo,
]
);
jsonOk([], 'Consentimiento quitado');
+78 -1
View File
@@ -843,6 +843,40 @@ require_once __DIR__ . '/../../../shared/components/sidebar.php';
</div>
</div>
<!-- ── Modal: quitar consentimiento ───────────────────────────── -->
<div id="modal-quitar-consent" style="
display:none;position:fixed;inset:0;z-index:9100;
background:rgba(0,0,0,.55);align-items:center;justify-content:center;padding:16px">
<div style="background:#fff;border-radius:14px;box-shadow:0 8px 40px rgba(0,0,0,.25);
width:min(96vw,480px);padding:24px;display:flex;flex-direction:column;gap:14px">
<div style="font-weight:700;font-size:.95rem;color:#1e293b">
<i class="fas fa-trash me-2 text-danger"></i>Quitar consentimiento
</div>
<div style="font-size:.85rem;color:#475569">
<strong id="mqc-nombre"></strong>
</div>
<div>
<label style="font-size:.82rem;font-weight:600;color:#374151;display:block;margin-bottom:4px">
Motivo <span style="color:#dc2626">*</span>
</label>
<textarea id="mqc-motivo" rows="3" placeholder="Explique por qué se quita este consentimiento…"
style="width:100%;border:1px solid #d1d5db;border-radius:8px;padding:8px 10px;
font-size:.85rem;resize:vertical;font-family:inherit"></textarea>
<div id="mqc-error" style="color:#dc2626;font-size:.78rem;margin-top:4px;display:none"></div>
</div>
<div style="display:flex;gap:8px;justify-content:flex-end">
<button onclick="_cerrarModalQuitarConsent()"
style="padding:7px 18px;border:1px solid #d1d5db;border-radius:8px;
background:#fff;cursor:pointer;font-size:.85rem">Cancelar</button>
<button id="mqc-btn-confirmar" onclick="_confirmarQuitarConsent()"
style="padding:7px 18px;border:none;border-radius:8px;
background:#dc2626;color:#fff;cursor:pointer;font-weight:600;font-size:.85rem">
<i class="fas fa-trash me-1"></i>Quitar
</button>
</div>
</div>
</div>
<!-- ── Modal de consentimiento embebido (modo embebido) ────────── -->
<div id="modal-consentimiento" style="
display:none;position:fixed;inset:0;z-index:9000;
@@ -1476,17 +1510,60 @@ function renderConsentimientos(lista) {
}
}
const btnQuitar = `<button class="btn btn-outline-danger" title="Quitar consentimiento"
onclick="_abrirModalQuitarConsent(${idJs}, ${nomJs.replace(/"/g,'&quot;')})">
<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}</div>
<div class="acciones-consent">${btnAccion}${btnVer}${btnQuitar}</div>
</div>`;
}).join('');
}
// ── Quitar consentimiento ─────────────────────────────────────
let _mqcConsentId = null;
function _abrirModalQuitarConsent(consentId, formNombre) {
_mqcConsentId = consentId;
document.getElementById('mqc-nombre').textContent = formNombre;
document.getElementById('mqc-motivo').value = '';
document.getElementById('mqc-error').style.display = 'none';
document.getElementById('modal-quitar-consent').style.display = 'flex';
setTimeout(() => document.getElementById('mqc-motivo').focus(), 50);
}
function _cerrarModalQuitarConsent() {
document.getElementById('modal-quitar-consent').style.display = 'none';
_mqcConsentId = null;
}
async function _confirmarQuitarConsent() {
const motivo = document.getElementById('mqc-motivo').value.trim();
const errEl = document.getElementById('mqc-error');
if (!motivo) { errEl.textContent = 'El motivo es obligatorio.'; errEl.style.display = ''; return; }
const btn = document.getElementById('mqc-btn-confirmar');
btn.disabled = true;
btn.innerHTML = '<i class="fas fa-spinner fa-spin me-1"></i>Quitando…';
try {
const res = await fetch(`${API}quitar_consentimiento.php`, {
method: 'POST',
headers: {'Content-Type':'application/json'},
body: JSON.stringify({consent_id: _mqcConsentId, motivo})
});
const json = await res.json();
if (!json.ok) { errEl.textContent = json.error || 'Error al quitar.'; errEl.style.display = ''; }
else {
_cerrarModalQuitarConsent();
if (turnoActivo?.id) actualizarConsentimientos(turnoActivo.id);
}
} catch(e) { errEl.textContent = 'Error de red.'; errEl.style.display = ''; }
btn.disabled = false;
btn.innerHTML = '<i class="fas fa-trash me-1"></i>Quitar';
}
async function actualizarConsentimientos(turnoId) {
if (!turnoId) return;
try {