feat: ciclos configurables + gestión de exámenes en Tomas Prolongadas
Feature 1 — Ciclos configurables por paciente: - ver_formulario_enviado.php: pantalla de configuración aparece la primera vez que el profesional abre el formulario (antes de firmar); permite seleccionar qué tomas realizar para cada protocolo. $_mpSkippedSepIds oculta las secciones no seleccionadas en render. $_mpMap se reconstruye desde los grupos filtrados para que next_firma_id salte tomas omitidas. - configurar_tomas.php (nuevo): guarda _tomas_config en datos_respuestas del consentimiento; valida que cada grupo tenga al menos una toma. - guardar_toma.php: aplica el filtro _tomas_config al array firmasCampos para que el conteo de tomas firmadas/pendientes sea correcto. Feature 2 — Gestión de tipos de examen (admin): - lab_tomas_config.php (nuevo): página admin que muestra los exámenes actuales con sus tomas y permite añadir nuevos tipos con su esquema completo (separador + hora + resultado + firma_profesional por toma). - api/lab/save_tomas_config.php (nuevo): modifica el esquema JSON del formulario id=15; add_exam agrega opción al selector y los campos de cada toma; delete_exam elimina la opción y los separadores exclusivos. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
3e110324a8
commit
2eefe3f5c8
@@ -0,0 +1,151 @@
|
||||
<?php
|
||||
/**
|
||||
* POST /api/lab/save_tomas_config.php
|
||||
* Gestiona los tipos de examen del formulario de Tomas Prolongadas (lab_formularios.id=15).
|
||||
*
|
||||
* Body JSON:
|
||||
* action string 'add_exam' | 'delete_exam'
|
||||
* exam_name string Nombre del nuevo tipo de examen
|
||||
* tomas array [{label, tipo:'minutos'|'hora_fija', valor}] (solo add_exam)
|
||||
*/
|
||||
require_once __DIR__ . '/_helpers.php';
|
||||
requireAdmin();
|
||||
requireMethod('POST');
|
||||
|
||||
$body = inputJson();
|
||||
$action = trim($body['action'] ?? '');
|
||||
|
||||
if (!in_array($action, ['add_exam', 'delete_exam'], true)) jsonError('action inválida.');
|
||||
|
||||
$db = Database::getInstance();
|
||||
$row = $db->fetch("SELECT id, esquema FROM lab_formularios WHERE id = 15 LIMIT 1");
|
||||
if (!$row) jsonError('Formulario de tomas no encontrado.', 404);
|
||||
|
||||
$esquema = json_decode($row['esquema'], true);
|
||||
if (!is_array($esquema)) jsonError('Esquema del formulario no válido.', 500);
|
||||
|
||||
// ── Localizar el campo selector de tipo de examen ────────────
|
||||
$idxSelector = null;
|
||||
foreach ($esquema as $i => $c) {
|
||||
if (($c['id'] ?? '') === '_c8j2g16') { $idxSelector = $i; break; }
|
||||
}
|
||||
if ($idxSelector === null) jsonError('Campo selector de examen (_c8j2g16) no encontrado.', 500);
|
||||
|
||||
// ── ADD EXAM ─────────────────────────────────────────────────
|
||||
if ($action === 'add_exam') {
|
||||
$examName = trim($body['exam_name'] ?? '');
|
||||
$tomas = $body['tomas'] ?? [];
|
||||
|
||||
if (!$examName) jsonError('exam_name requerido.');
|
||||
if (strlen($examName) > 80) jsonError('exam_name demasiado largo (máx 80 chars).');
|
||||
if (!is_array($tomas) || empty($tomas)) jsonError('tomas requeridas.');
|
||||
if (count($tomas) > 20) jsonError('Máximo 20 tomas por examen.');
|
||||
|
||||
// Verificar que el examen no exista ya
|
||||
$currentOptions = $esquema[$idxSelector]['options'] ?? [];
|
||||
if (in_array($examName, $currentOptions, true)) {
|
||||
jsonError("El tipo de examen '$examName' ya existe.");
|
||||
}
|
||||
|
||||
// Validar tomas
|
||||
foreach ($tomas as $i => $t) {
|
||||
$tipo = $t['tipo'] ?? '';
|
||||
$valor = $t['valor'] ?? '';
|
||||
$label = trim($t['label'] ?? '');
|
||||
if (!in_array($tipo, ['minutos', 'hora_fija'], true)) jsonError("Toma $i: tipo inválido.");
|
||||
if (!$label) jsonError("Toma $i: label requerido.");
|
||||
if ($tipo === 'minutos' && (!is_numeric($valor) || (int)$valor < 0))
|
||||
jsonError("Toma $i: valor de minutos inválido.");
|
||||
if ($tipo === 'hora_fija' && !preg_match('/^\d{1,2}:\d{2}$/', $valor))
|
||||
jsonError("Toma $i: hora_fija debe ser HH:MM.");
|
||||
}
|
||||
|
||||
// Prefijo corto para IDs (basado en nombre del examen, sanitizado)
|
||||
$prefix = '_' . substr(preg_replace('/[^a-z0-9]/i', '', strtolower($examName)), 0, 8) . '_';
|
||||
$uid = substr(md5($examName . microtime()), 0, 4);
|
||||
|
||||
// 1. Agregar opción al campo selector
|
||||
$esquema[$idxSelector]['options'][] = $examName;
|
||||
|
||||
// 2. Agregar campos al final del esquema
|
||||
$condCampoId = '_c8j2g16';
|
||||
foreach ($tomas as $idx => $t) {
|
||||
$label = trim($t['label']);
|
||||
$tipo = $t['tipo'];
|
||||
$valor = $t['valor'];
|
||||
|
||||
// Construir label del separador
|
||||
if ($tipo === 'minutos') {
|
||||
$sepLabel = "$examName · Minuto $valor";
|
||||
} else {
|
||||
// hora_fija: convertir HH:MM a "H:MM a.m./p.m."
|
||||
[$hh, $mm] = explode(':', $valor);
|
||||
$h = (int)$hh; $ampm = $h >= 12 ? 'p.m.' : 'a.m.';
|
||||
$h12 = $h > 12 ? $h - 12 : ($h === 0 ? 12 : $h);
|
||||
$sepLabel = "$examName · {$h12}:{$mm} {$ampm}";
|
||||
}
|
||||
|
||||
$sepId = $prefix . 's' . $idx . $uid;
|
||||
$horaId = $prefix . 'h' . $idx . $uid;
|
||||
$resId = $prefix . 'r' . $idx . $uid;
|
||||
$firmaId = $prefix . 'f' . $idx . $uid;
|
||||
|
||||
$esquema[] = [
|
||||
'id' => $sepId,
|
||||
'tipo' => 'separador',
|
||||
'label' => $sepLabel,
|
||||
'condicion'=> ['campo_id' => $condCampoId, 'valores' => [$examName]],
|
||||
];
|
||||
$esquema[] = [
|
||||
'id' => $horaId,
|
||||
'tipo' => 'hora',
|
||||
'label' => 'Hora de toma',
|
||||
];
|
||||
$esquema[] = [
|
||||
'id' => $resId,
|
||||
'tipo' => 'numero',
|
||||
'label' => 'Resultado',
|
||||
];
|
||||
$esquema[] = [
|
||||
'id' => $firmaId,
|
||||
'tipo' => 'firma_profesional',
|
||||
'label' => 'Firma del profesional',
|
||||
];
|
||||
}
|
||||
|
||||
$db->getConnection()->prepare(
|
||||
"UPDATE lab_formularios SET esquema = ? WHERE id = 15"
|
||||
)->execute([json_encode($esquema, JSON_UNESCAPED_UNICODE)]);
|
||||
|
||||
jsonOk([
|
||||
'exam_name' => $examName,
|
||||
'tomas_count' => count($tomas),
|
||||
'options_count'=> count($esquema[$idxSelector]['options']),
|
||||
], "Examen '$examName' agregado con " . count($tomas) . " tomas.");
|
||||
}
|
||||
|
||||
// ── DELETE EXAM ──────────────────────────────────────────────
|
||||
if ($action === 'delete_exam') {
|
||||
$examName = trim($body['exam_name'] ?? '');
|
||||
if (!$examName) jsonError('exam_name requerido.');
|
||||
|
||||
$options = $esquema[$idxSelector]['options'] ?? [];
|
||||
if (!in_array($examName, $options, true)) jsonError("El examen '$examName' no existe.");
|
||||
|
||||
// Eliminar opción del selector
|
||||
$esquema[$idxSelector]['options'] = array_values(array_filter($options, fn($o) => $o !== $examName));
|
||||
|
||||
// Eliminar separadores condicionados únicamente a este examen
|
||||
$esquema = array_values(array_filter($esquema, function($c) use ($examName) {
|
||||
$cond = $c['condicion'] ?? null;
|
||||
if (!$cond) return true;
|
||||
$vals = $cond['valores'] ?? [];
|
||||
return !(count($vals) === 1 && $vals[0] === $examName);
|
||||
}));
|
||||
|
||||
$db->getConnection()->prepare(
|
||||
"UPDATE lab_formularios SET esquema = ? WHERE id = 15"
|
||||
)->execute([json_encode($esquema, JSON_UNESCAPED_UNICODE)]);
|
||||
|
||||
jsonOk(['exam_name' => $examName], "Examen '$examName' eliminado.");
|
||||
}
|
||||
Reference in New Issue
Block a user