feat: panel configurar tiempos con chips en F-LAB-28

- Schema F-LAB-28: reemplaza 9 secciones hardcodeadas por 17 puntos de
  tiempo (0,15,30,...,195 min, 3pm, 4pm, Otro)
- PHP: detecta sección "Otro" como toma válida (sentinel PHP_INT_MAX)
- JS/CSS: reemplaza checkboxes por chips clicables en el panel de
  configuración; chip "Otro" muestra input de hora libre

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Lizandro Guarnizo
2026-07-17 16:33:19 -05:00
co-authored by Claude Sonnet 4.6
parent 95acc0a09c
commit b280dfcb7a
+57 -20
View File
@@ -279,6 +279,8 @@ if ($modoTurnero && $embebido && $_soloFirmaPro) {
if ($_pm && $_h < 12) $_h += 12;
$_mpHoraFija = sprintf('%02d:%02d', $_h, $_m2);
$_mpMin = PHP_INT_MAX; // sentinel: va después de todos los Minuto X
} elseif (stripos($_mpLabel, 'Otro') !== false) {
$_mpMin = PHP_INT_MAX; // sentinel: toma de hora libre
}
} elseif ($_t === 'hora' && ($_mpMin !== null || $_mpHoraFija !== null)) {
$_mpHora = $_c['id'] ?? null;
@@ -628,6 +630,13 @@ function _addExamWizardHtml(string $cid): string {
font-size:1rem;font-weight:700; }
.mp-cfg-body { padding:1.25rem; }
.mp-cfg-foot { padding:0 1.25rem 1.25rem; }
.mp-chips { display:flex;flex-wrap:wrap;gap:.4rem;padding:.25rem 0; }
.mp-chip { padding:.3rem .75rem;border:1.5px solid #cbd5e1;border-radius:2rem;
background:#f8fafc;color:#475569;font-size:.82rem;cursor:pointer;transition:all .15s;line-height:1.4; }
.mp-chip.active { background:#1e3a5f;border-color:#1e3a5f;color:#fff;font-weight:600; }
.mp-chip.signed { background:#f0fdf4;border-color:#86efac;color:#166534;cursor:default; }
.mp-chip:not(.signed):not(:disabled):hover { border-color:#1e3a5f; }
.mp-chip-otro-inp { display:none;width:100px;vertical-align:middle;margin-left:.3rem; }
.mp-cfg-gtitle { font-weight:700;font-size:.88rem;color:#1e3a5f;margin-bottom:.5rem;
border-left:3px solid <?= htmlspecialchars($docColor) ?>;padding-left:.5rem; }
.mp-cfg-item { border:1.5px solid #e5e7eb;border-radius:8px;padding:.35rem .6rem;
@@ -1346,41 +1355,69 @@ function _mpGuardarConfig(config, examType, onOk, onErr) {
.catch(function() { onErr('Error de conexión.'); });
}
function _mpChipLabel(label) {
var m = label.match(/Minuto\s+(\d+)/i);
if (m) return m[1];
var pm = label.match(/(\d{1,2}):\d{2}\s*p\.?m/i);
if (pm) return pm[1] + 'pm';
if (/otro/i.test(label)) return 'Otro';
return label.replace(/^Toma\s*·\s*/i, '').trim();
}
function _mpShowCfgPanel(examName, groups, isModify) {
// Construir checkboxes
var savedFirmas = window._mpSavedFirmas || {};
var html = '';
var multiGrupo = Object.keys(groups).length > 1;
for (var gk in groups) {
var secs = groups[gk];
var title = secs[0] ? (secs[0].display || gk) : gk;
html += '<div class="mb-3">'
+ (multiGrupo ? '<div class="mp-cfg-gtitle">' + title + '</div>' : '')
+ '<div class="row g-2">';
+ (multiGrupo ? '<div class="mp-cfg-gtitle">' + (secs[0] ? (secs[0].display || gk) : gk) + '</div>' : '')
+ '<div class="mp-chips">';
for (var i = 0; i < secs.length; i++) {
var s = secs[i];
var isSigned = !!savedFirmas[s.firma];
var inSaved = isModify && _mpTomasConfig && _mpTomasConfig[gk]
&& _mpTomasConfig[gk].indexOf(s.firma) !== -1;
var isChecked = isSigned || inSaved;
html += '<div class="col-6 col-sm-4"><div class="mp-cfg-item form-check' + (isSigned ? ' opacity-50' : '') + '">'
+ '<input class="form-check-input mp-cfg-chk" type="checkbox"'
+ ' id="cfg-' + s.firma + '" value="' + s.firma + '" data-group="' + gk + '"'
+ (isChecked ? ' checked' : '')
+ (isSigned ? ' disabled title="Ya firmada"' : '') + '>'
+ '<label class="form-check-label small" for="cfg-' + s.firma + '">'
+ (s.label || ('Toma ' + (i+1))) + (isSigned ? ' <i class=\'fas fa-check text-success\'></i>' : '') + '</label>'
+ '</div></div>';
var isSigned = !!savedFirmas[s.firma];
var inSaved = isModify && _mpTomasConfig && _mpTomasConfig[gk]
&& _mpTomasConfig[gk].indexOf(s.firma) !== -1;
var isActive = isSigned || inSaved;
var isOtro = /otro/i.test(s.label || '');
var chipLbl = _mpChipLabel(s.label || ('Toma ' + (i+1)));
// checkbox oculto — sigue siendo la fuente de verdad para el guardado
html += '<input type="checkbox" class="mp-cfg-chk d-none" id="cfg-' + s.firma + '"'
+ ' value="' + s.firma + '" data-group="' + gk + '"'
+ (isActive ? ' checked' : '') + (isSigned ? ' disabled' : '') + '>';
html += '<button type="button" class="mp-chip' + (isActive ? ' active' : '') + (isSigned ? ' signed' : '') + '"'
+ ' data-target="cfg-' + s.firma + '"' + (isOtro ? ' data-otro="1"' : '')
+ (isSigned ? ' disabled title="Ya firmada"' : '') + '>'
+ chipLbl + (isSigned ? ' <i class="fas fa-check ms-1 small"></i>' : '') + '</button>';
if (isOtro) {
html += '<input type="text" class="form-control form-control-sm mp-chip-otro-inp"'
+ ' placeholder="Hora..."' + (isActive ? ' style="display:inline-block"' : '') + '>';
}
}
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;
var grupos = document.getElementById('mp-cfg-grupos');
grupos.innerHTML = html;
document.getElementById('mp-cfg-title').innerHTML = '<i class="fas fa-sliders-h me-2"></i>' + examName;
document.getElementById('mp-cfg-overlay').classList.remove('d-none');
}
// Click en chips de tiempo (delegado al documento para evitar múltiples listeners)
document.addEventListener('click', function(e) {
var chip = e.target.closest('.mp-chip');
if (!chip || chip.disabled || !chip.closest('#mp-cfg-grupos')) return;
var chk = document.getElementById(chip.getAttribute('data-target'));
if (!chk || chk.disabled) return;
chk.checked = !chk.checked;
chip.classList.toggle('active', chk.checked);
if (chip.dataset.otro) {
var inp = chip.nextElementSibling;
if (inp && inp.classList.contains('mp-chip-otro-inp'))
inp.style.display = chk.checked ? 'inline-block' : 'none';
}
});
function _mpGetGroupsForExam(examName) {
var result = {};
for (var gk in _mpAllGroups) {