Tomas: panel de ciclos dinámico activado al seleccionar tipo de examen

- Panel ya no se renderiza server-side, sino que JS lo muestra al instante
  cuando el usuario selecciona un tipo de examen del selector _c8j2g16
- _mpGetGroupsForExam() filtra los grupos correspondientes al examen elegido
- Si solo hay 1 toma por grupo, auto-guarda sin mostrar panel
- Si al cargar ya hay examen seleccionado y sin _tomas_config, muestra panel
- configurar_tomas.php: guarda también _c8j2g16 (exam_type) en datos_respuestas

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Lizandro Guarnizo
2026-07-10 11:19:55 -05:00
co-authored by Claude Sonnet 4.6
parent f0fb0c7492
commit da2b67589e
2 changed files with 120 additions and 110 deletions
+5 -3
View File
@@ -11,9 +11,10 @@
require_once __DIR__ . '/_helpers.php';
requireMethod('POST');
$body = inputJson();
$token = trim($body['token'] ?? '');
$cfg = $body['tomas_config'] ?? null;
$body = inputJson();
$token = trim($body['token'] ?? '');
$cfg = $body['tomas_config'] ?? null;
$examType = trim($body['exam_type'] ?? '');
if (!$token) jsonError('token requerido.');
if (!is_array($cfg) || empty($cfg)) jsonError('tomas_config requerido.');
@@ -40,6 +41,7 @@ if ($tc['datos_respuestas']) {
}
$dr['_tomas_config'] = $cfg;
if ($examType !== '') $dr['_c8j2g16'] = $examType;
$pdo->prepare("UPDATE turnero_consentimientos SET datos_respuestas = ? WHERE id = ?")
->execute([json_encode($dr, JSON_UNESCAPED_UNICODE), $tc['id']]);
+115 -107
View File
@@ -685,40 +685,17 @@ function _addExamWizardHtml(string $cid): string {
</div>
<?php
// ── Panel de configuración de ciclos (se muestra antes de iniciar el protocolo) ──
// Datos para el panel de ciclos dinámico (renderizado por JS al seleccionar examen)
$_examOptions = [];
foreach ($esquema as $_oc) {
if (($_oc['id'] ?? '') === '_c8j2g16') { $_examOptions = $_oc['options'] ?? []; break; }
}
$_examSel = (array)($datosCliente['_c8j2g16'] ?? []);
$_gruposConfig = [];
foreach ($_mpGroupsFull as $_gk => $_gsecs) {
if (empty($_gsecs)) continue;
$__parts = array_values(array_filter(explode('|', $_gk)));
$__isCond = !empty(array_intersect($__parts, $_examOptions));
$__matchSel = !empty(array_intersect($__parts, $_examSel));
if (!$__isCond || $__matchSel) {
$__title = implode(' / ', array_intersect($__parts, $_examSel));
if (!$__title) $__title = $_gsecs[0]['display'] ?? $_gk;
$_gruposConfig[$_gk] = ['title' => $__title, 'secs' => $_gsecs];
}
}
// Mostrar panel solo si al menos un grupo tiene >= 2 tomas (hay algo que configurar).
// Si todos los grupos tienen 1 sola toma, el JS auto-guarda la config sin preguntar.
$_hasMulTomas = array_reduce($_gruposConfig, fn($c, $g) => $c || count($g['secs']) >= 2, false);
$_showCfgPanel = $modoTurnero && $embebido && !empty($_gruposConfig) && $_tomasConfig === null && $_hasMulTomas;
$_autoSaveCfg = $modoTurnero && $embebido && !empty($_gruposConfig) && $_tomasConfig === null && !$_hasMulTomas;
// Config por defecto (todas las tomas) — usada tanto por auto-save como por "Usar todas"
$_cfgDefault = [];
foreach ($_gruposConfig as $_cgk => $_cg) {
$_cfgDefault[$_cgk] = array_column($_cg['secs'], 'firma');
}
?>
<?php if ($_showCfgPanel): ?>
<div class="mp-cfg-overlay" id="mp-cfg-overlay">
<!-- Panel de ciclos — renderizado dinámicamente por JS al seleccionar tipo de examen -->
<div class="mp-cfg-overlay d-none" id="mp-cfg-overlay">
<div class="mp-cfg-card">
<div class="mp-cfg-head d-flex align-items-center justify-content-between">
<span><i class="fas fa-sliders-h me-2"></i>Configurar protocolo de tomas</span>
<span id="mp-cfg-title"><i class="fas fa-sliders-h me-2"></i>Configurar protocolo de tomas</span>
<button id="mp-cfg-todas" type="button"
title="Usar todas las tomas sin configurar"
style="background:rgba(255,255,255,.2);border:none;color:#fff;border-radius:6px;
@@ -730,27 +707,7 @@ function _addExamWizardHtml(string $cid): string {
<p class="text-muted small mb-3">
Seleccione las tomas a realizar. Desmárquelas si el médico indicó un protocolo reducido.
</p>
<?php foreach ($_gruposConfig as $_cgk => $_cg): ?>
<div class="mb-3">
<div class="mp-cfg-gtitle"><?= esc2($_cg['title']) ?></div>
<div class="row g-2">
<?php foreach ($_cg['secs'] as $_cs): ?>
<div class="col-6 col-sm-4">
<div class="mp-cfg-item form-check">
<input class="form-check-input mp-cfg-chk" type="checkbox"
id="cfg-<?= esc2($_cs['firma']) ?>"
value="<?= esc2($_cs['firma']) ?>"
data-group="<?= esc2($_cgk) ?>"
checked>
<label class="form-check-label small" for="cfg-<?= esc2($_cs['firma']) ?>">
<?= esc2($_cs['label']) ?>
</label>
</div>
</div>
<?php endforeach; ?>
</div>
</div>
<?php endforeach; ?>
<div id="mp-cfg-grupos"></div>
</div>
<div class="mp-cfg-foot">
<button class="btn btn-success btn-lg w-100" id="mp-cfg-iniciar">
@@ -760,7 +717,6 @@ function _addExamWizardHtml(string $cid): string {
</div>
</div>
</div>
<?php endif; ?>
<div class="doc-body">
@@ -1266,79 +1222,131 @@ function _addExamWizardHtml(string $cid): string {
<script>
<?php if (!empty($_mpMap)): ?>
/* ── Panel configuración de ciclos + auto-save ── */
<?php if (($_showCfgPanel || $_autoSaveCfg) ?? false): ?>
var _mpCfgDefault = <?= json_encode($_cfgDefault, JSON_UNESCAPED_UNICODE) ?>;
var _mpCfgToken = <?= json_encode($tokenTurnero) ?>;
/* ── Panel de ciclos dinámico ── */
var _mpAllGroups = <?= json_encode($_mpGroupsFull, JSON_UNESCAPED_UNICODE) ?>;
var _mpExamOptions = <?= json_encode($_examOptions, JSON_UNESCAPED_UNICODE) ?>;
var _mpTomasConfig = <?= json_encode($_tomasConfig, JSON_UNESCAPED_UNICODE) ?>;
var _mpCfgToken = <?= json_encode($tokenTurnero, JSON_UNESCAPED_UNICODE) ?>;
var _mpModoTurnero = <?= json_encode($modoTurnero && $embebido) ?>;
function _mpGuardarConfig(config, onOk, onErr) {
function _mpGuardarConfig(config, examType, onOk, onErr) {
var base = window.location.href.split('/ver_formulario_enviado.php')[0];
fetch(base + '/modules/turnero/api/configurar_tomas.php', {
method: 'POST', headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ token: _mpCfgToken, tomas_config: config })
body: JSON.stringify({ token: _mpCfgToken, tomas_config: config, exam_type: examType })
})
.then(function(r) { return r.json(); })
.then(function(d) { d.ok ? onOk() : onErr(d.error || 'Error desconocido'); })
.catch(function() { onErr('Error de conexión.'); });
}
<?php if ($_autoSaveCfg ?? false): ?>
// Todos los grupos tienen una sola toma → guardar config default sin mostrar panel
function _mpShowCfgPanel(examName, groups) {
// Construir checkboxes
var html = '';
for (var gk in groups) {
var secs = groups[gk];
var title = secs[0] ? (secs[0].display || gk) : gk;
html += '<div class="mb-3"><div class="mp-cfg-gtitle">' + title + '</div><div class="row g-2">';
for (var i = 0; i < secs.length; i++) {
var s = secs[i];
html += '<div class="col-6 col-sm-4"><div class="mp-cfg-item form-check">'
+ '<input class="form-check-input mp-cfg-chk" type="checkbox"'
+ ' id="cfg-' + s.firma + '" value="' + s.firma + '" data-group="' + gk + '" checked>'
+ '<label class="form-check-label small" for="cfg-' + s.firma + '">'
+ (s.label || ('Toma ' + (i+1))) + '</label>'
+ '</div></div>';
}
html += '</div></div>';
}
document.getElementById('mp-cfg-grupos').innerHTML = html;
document.getElementById('mp-cfg-title').innerHTML =
'<i class="fas fa-sliders-h me-2"></i>' + examName;
// Si solo hay 1 toma por grupo, auto-guardar sin mostrar panel
var hasMul = Object.keys(groups).some(function(k) { return groups[k].length >= 2; });
if (!hasMul) {
var cfg = {};
for (var gk in groups) cfg[gk] = groups[gk].map(function(s){return s.firma;});
_mpGuardarConfig(cfg, examName, function() { location.reload(); }, function(e) {
console.warn('mp-cfg auto:', e);
});
return;
}
document.getElementById('mp-cfg-overlay').classList.remove('d-none');
}
function _mpGetGroupsForExam(examName) {
var result = {};
for (var gk in _mpAllGroups) {
var parts = gk.split('|').filter(Boolean);
var isCond = parts.some(function(p) { return _mpExamOptions.indexOf(p) !== -1; });
if (!isCond || parts.indexOf(examName) !== -1) {
result[gk] = _mpAllGroups[gk];
}
}
return result;
}
// Escuchar selección de tipo de examen
document.addEventListener('change', function(e) {
if (!_mpModoTurnero) return;
if (_mpTomasConfig !== null) return; // ya configurado
var el = e.target;
if (el.type !== 'radio' || el.name !== '_c8j2g16') return;
var examName = el.value;
var groups = _mpGetGroupsForExam(examName);
if (!Object.keys(groups).length) return;
_mpShowCfgPanel(examName, groups);
});
// Si al cargar ya hay examen seleccionado pero sin _tomas_config → mostrar panel
document.addEventListener('DOMContentLoaded', function() {
_mpGuardarConfig(_mpCfgDefault, function() { location.reload(); }, function(e) {
console.warn('mp-cfg auto-save:', e);
if (!_mpModoTurnero || _mpTomasConfig !== null) return;
var radio = document.querySelector('[name="_c8j2g16"]:checked');
if (!radio) return;
var groups = _mpGetGroupsForExam(radio.value);
if (Object.keys(groups).length) _mpShowCfgPanel(radio.value, groups);
});
// Botón "Usar todas"
document.getElementById('mp-cfg-todas').addEventListener('click', function() {
var examName = (document.querySelector('[name="_c8j2g16"]:checked') || {}).value || '';
var cfg = {};
document.querySelectorAll('.mp-cfg-chk').forEach(function(chk) {
if (!cfg[chk.dataset.group]) cfg[chk.dataset.group] = [];
cfg[chk.dataset.group].push(chk.value);
});
var btn = this;
btn.disabled = true; btn.innerHTML = '<i class="fas fa-spinner fa-spin me-2"></i>Guardando...';
_mpGuardarConfig(cfg, examName, function() { location.reload(); }, function(e) {
document.getElementById('mp-cfg-msg').innerHTML = '<span class="text-danger">' + e + '</span>';
btn.disabled = false; btn.innerHTML = '<i class="fas fa-forward me-1"></i>Usar todas';
});
});
<?php endif; ?>
<?php if ($_showCfgPanel ?? false): ?>
(function() {
function showMsg(msg) {
var el = document.getElementById('mp-cfg-msg');
if (el) el.innerHTML = '<span class="text-danger"><i class="fas fa-exclamation-circle me-1"></i>' + msg + '</span>';
// Botón "Iniciar protocolo"
document.getElementById('mp-cfg-iniciar').addEventListener('click', function() {
var examName = (document.querySelector('[name="_c8j2g16"]:checked') || {}).value || '';
var byGroup = {};
document.querySelectorAll('.mp-cfg-chk').forEach(function(chk) {
if (!byGroup[chk.dataset.group]) byGroup[chk.dataset.group] = [];
if (chk.checked) byGroup[chk.dataset.group].push(chk.value);
});
for (var g in byGroup) {
if (!byGroup[g].length) {
document.getElementById('mp-cfg-msg').innerHTML =
'<span class="text-danger"><i class="fas fa-exclamation-circle me-1"></i>Seleccione al menos una toma por protocolo.</span>';
return;
}
}
function setBtnLoading(btn) {
btn.disabled = true;
btn.innerHTML = '<i class="fas fa-spinner fa-spin me-2"></i>Guardando...';
}
// Botón "Usar todas" → guarda config default y recarga
var btnTodas = document.getElementById('mp-cfg-todas');
if (btnTodas) {
btnTodas.addEventListener('click', function() {
setBtnLoading(this);
_mpGuardarConfig(_mpCfgDefault, function() { location.reload(); }, function(e) {
showMsg(e);
btnTodas.disabled = false;
btnTodas.innerHTML = '<i class="fas fa-forward me-1"></i>Usar todas';
});
});
}
// Botón "Iniciar protocolo" → lee selección actual
var btnIniciar = document.getElementById('mp-cfg-iniciar');
if (btnIniciar) {
btnIniciar.addEventListener('click', function() {
var byGroup = {};
document.querySelectorAll('.mp-cfg-chk').forEach(function(chk) {
var g = chk.dataset.group;
if (!byGroup[g]) byGroup[g] = [];
if (chk.checked) byGroup[g].push(chk.value);
});
for (var g in byGroup) {
if (!byGroup[g].length) { showMsg('Seleccione al menos una toma por protocolo.'); return; }
}
setBtnLoading(this);
_mpGuardarConfig(byGroup, function() { location.reload(); }, function(e) {
showMsg(e);
btnIniciar.disabled = false;
btnIniciar.innerHTML = '<i class="fas fa-play-circle me-2"></i>Iniciar protocolo';
});
});
}
})();
<?php endif; ?>
<?php endif; ?>
var btn = this;
btn.disabled = true; btn.innerHTML = '<i class="fas fa-spinner fa-spin me-2"></i>Guardando...';
_mpGuardarConfig(byGroup, examName, function() { location.reload(); }, function(e) {
document.getElementById('mp-cfg-msg').innerHTML = '<span class="text-danger">' + e + '</span>';
btn.disabled = false; btn.innerHTML = '<i class="fas fa-play-circle me-2"></i>Iniciar protocolo';
});
});
/* ── Wizard: añadir tipo de examen ── */
var _aewState = {}; // {cid: {count, rows}}