Turnero: mejoras UI, historial detallado, kiosko scanner, firma por turno
- Kiosko: detector scanner (gap < 80ms), iconos 90px, fa-person-pregnant, ticket Arial - Recepción: vincular_paciente.php para persistir paciente_id antes de firmar consentimiento - Historial y dashboard: recepción desk, empleado recepción/muestras, exámenes y comentarios - display_global: turno más pequeño, lugar-nombre más grande - lab_pacientes: modal compacta con form-sm - Configuración: icono embarazada fa-person-pregnant en prioridades - ver_formulario_enviado: firma por campo (fix doble firma) - docker-compose.local.yml: override BD producción para dev local Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
ebbf4ac6cf
commit
f363631b32
@@ -124,13 +124,20 @@ $stmtTurnos = $pdo->prepare(
|
||||
t.creado_at, t.inicio_recepcion_at, t.fin_recepcion_at,
|
||||
t.inicio_lugar_at, t.fin_lugar_at,
|
||||
p.codigo AS prioridad_codigo, p.nombre AS prioridad_nombre, p.color AS prioridad_color,
|
||||
l.nombre AS lugar_nombre,
|
||||
l.nombre AS lugar_nombre,
|
||||
s.nombre_completo AS paciente_bd,
|
||||
rd.nombre AS recepcion_desk_nombre,
|
||||
ur.full_name AS atendido_recepcion_nombre,
|
||||
ul.full_name AS atendido_lugar_nombre,
|
||||
ts.observaciones AS notas,
|
||||
ROUND(TIMESTAMPDIFF(SECOND, t.creado_at, COALESCE(t.inicio_recepcion_at, NOW())) / 60.0, 1) AS espera_min,
|
||||
ROUND(TIMESTAMPDIFF(SECOND, t.inicio_lugar_at, COALESCE(t.fin_lugar_at, NOW())) / 60.0, 1) AS servicio_min
|
||||
FROM turnero_turnos t
|
||||
JOIN turnero_prioridades p ON p.id = t.prioridad_id
|
||||
LEFT JOIN turnero_lugares l ON l.id = t.lugar_destino_id
|
||||
LEFT JOIN turnero_lugares l ON l.id = t.lugar_destino_id
|
||||
LEFT JOIN turnero_lugares rd ON rd.id = t.recepcion_desk_id
|
||||
LEFT JOIN admin_users ur ON ur.id = t.atendido_recepcion_por
|
||||
LEFT JOIN admin_users ul ON ul.id = t.atendido_lugar_por
|
||||
LEFT JOIN turnero_solicitudes ts ON ts.turno_id = t.id
|
||||
LEFT JOIN lab_pacientes s ON s.id = ts.paciente_id
|
||||
WHERE t.sesion_id = ?
|
||||
@@ -139,6 +146,25 @@ $stmtTurnos = $pdo->prepare(
|
||||
$stmtTurnos->execute([$sesionId]);
|
||||
$turnos = $stmtTurnos->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
// Enriquecer con exámenes y comentarios
|
||||
foreach ($turnos as &$turno) {
|
||||
$stmtEx = $pdo->prepare(
|
||||
"SELECT DISTINCT et.nombre FROM turnero_examen_items tei
|
||||
JOIN exam_tipos et ON et.id = tei.exam_tipo_id
|
||||
WHERE tei.solicitud_id = (SELECT id FROM turnero_solicitudes WHERE turno_id = ? LIMIT 1)"
|
||||
);
|
||||
$stmtEx->execute([$turno['id']]);
|
||||
$turno['examenes'] = $stmtEx->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
$stmtCom = $pdo->prepare(
|
||||
"SELECT usuario_nombre, comentario, tipo, creado_at
|
||||
FROM turnero_comentarios WHERE turno_id = ? ORDER BY creado_at ASC"
|
||||
);
|
||||
$stmtCom->execute([$turno['id']]);
|
||||
$turno['comentarios'] = $stmtCom->fetchAll(PDO::FETCH_ASSOC);
|
||||
}
|
||||
unset($turno);
|
||||
|
||||
// ── 6. Consentimientos del día ────────────────────────────────
|
||||
$stmtcons = $pdo->prepare(
|
||||
"SELECT tc.estado, COUNT(*) AS cnt
|
||||
|
||||
@@ -108,6 +108,9 @@ $sqlTurnos = "
|
||||
p.color AS prioridad_color,
|
||||
l.nombre AS lugar_nombre,
|
||||
s2.nombre_completo AS paciente_bd,
|
||||
rd.nombre AS recepcion_desk_nombre,
|
||||
ur.full_name AS atendido_recepcion_nombre,
|
||||
ul.full_name AS atendido_lugar_nombre,
|
||||
ROUND(TIMESTAMPDIFF(SECOND, t.creado_at,
|
||||
COALESCE(t.inicio_recepcion_at, t.fin_recepcion_at)) / 60.0, 1)
|
||||
AS espera_min,
|
||||
@@ -118,6 +121,9 @@ $sqlTurnos = "
|
||||
JOIN turnero_sesiones s ON s.id = t.sesion_id
|
||||
JOIN turnero_prioridades p ON p.id = t.prioridad_id
|
||||
LEFT JOIN turnero_lugares l ON l.id = t.lugar_destino_id
|
||||
LEFT JOIN turnero_lugares rd ON rd.id = t.recepcion_desk_id
|
||||
LEFT JOIN admin_users ur ON ur.id = t.atendido_recepcion_por
|
||||
LEFT JOIN admin_users ul ON ul.id = t.atendido_lugar_por
|
||||
LEFT JOIN turnero_solicitudes ts2 ON ts2.turno_id = t.id
|
||||
LEFT JOIN lab_pacientes s2 ON s2.id = ts2.paciente_id
|
||||
$whereStr
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
/**
|
||||
* POST { turno_id, paciente_id }
|
||||
* Vincula un paciente al turno para que los consentimientos puedan crearse.
|
||||
*/
|
||||
require_once __DIR__ . '/_helpers.php';
|
||||
requireMethod('POST');
|
||||
requireTurnero();
|
||||
|
||||
$d = inputJson();
|
||||
$turnoId = (int)($d['turno_id'] ?? 0);
|
||||
$pacienteId = (int)($d['paciente_id'] ?? 0);
|
||||
|
||||
if (!$turnoId || !$pacienteId) jsonError('turno_id y paciente_id requeridos.');
|
||||
|
||||
$pdo = db();
|
||||
$stmt = $pdo->prepare('UPDATE turnero_turnos SET paciente_id = ? WHERE id = ?');
|
||||
$stmt->execute([$pacienteId, $turnoId]);
|
||||
|
||||
if (!$stmt->rowCount()) jsonError('Turno no encontrado.', 404);
|
||||
|
||||
jsonOk(['turno_id' => $turnoId, 'paciente_id' => $pacienteId]);
|
||||
@@ -623,7 +623,8 @@ $tab = $_GET['tab'] ?? 'lugares';
|
||||
<select id="edit-pr-icono" class="form-select form-select-sm">
|
||||
<option value="">— Sin icono —</option>
|
||||
<option value="fas fa-child">👶 Niños (fas fa-child)</option>
|
||||
<option value="fas fa-heart">❤ Embarazada (fas fa-heart)</option>
|
||||
<option value="fa-solid fa-person-pregnant">🤰 Embarazada (fa-person-pregnant)</option>
|
||||
<option value="fas fa-heart">❤ Embarazada alt (fas fa-heart)</option>
|
||||
<option value="fas fa-person-cane">🦯 Adulto mayor (fas fa-person-cane)</option>
|
||||
<option value="fas fa-wheelchair">♿ Discapacidad (fas fa-wheelchair)</option>
|
||||
<option value="fas fa-user">👤 Paciente general (fas fa-user)</option>
|
||||
|
||||
@@ -137,6 +137,7 @@ Layout::open('Dashboard Turnero', 'fas fa-chart-bar');
|
||||
<th>Espera</th>
|
||||
<th>Servicio</th>
|
||||
<th>Entrada</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="tbodyTurnos">
|
||||
@@ -324,7 +325,8 @@ function renderTabla(turnos) {
|
||||
const hora = t.creado_at ? new Date(t.creado_at.replace(' ', 'T')).toLocaleTimeString('es-CO', {hour:'2-digit',minute:'2-digit'}) : '—';
|
||||
const espMin = t.espera_min !== null ? t.espera_min + ' m' : '—';
|
||||
const srvMin = t.servicio_min !== null ? t.servicio_min + ' m' : '—';
|
||||
return `<tr>
|
||||
const rowId = `dash-det-${t.id}`;
|
||||
return `<tr style="cursor:pointer" onclick="toggleDashDetalle('${rowId}', this)">
|
||||
<td><span class="fw-bold" style="color:${escHtml(t.prioridad_color || '#333')}">${escHtml(t.codigo)}</span></td>
|
||||
<td><span class="estado-badge" style="background:${escHtml(t.prioridad_color+'22'||'#eee')};color:${escHtml(t.prioridad_color||'#333')}">${escHtml(t.prioridad_codigo)}</span></td>
|
||||
<td>${nombrePac}</td>
|
||||
@@ -333,10 +335,77 @@ function renderTabla(turnos) {
|
||||
<td>${espMin}</td>
|
||||
<td>${srvMin}</td>
|
||||
<td>${hora}</td>
|
||||
<td><i class="fas fa-chevron-down text-muted" style="font-size:.65rem"></i></td>
|
||||
</tr>
|
||||
<tr id="${rowId}" style="display:none;background:#f8fafc">
|
||||
<td colspan="9" style="padding:0">
|
||||
<div style="padding:12px 16px;border-top:1px solid #e2e8f0">
|
||||
${renderDashDetalle(t)}
|
||||
</div>
|
||||
</td>
|
||||
</tr>`;
|
||||
}).join('');
|
||||
}
|
||||
|
||||
function toggleDashDetalle(rowId, tr) {
|
||||
const det = document.getElementById(rowId);
|
||||
const icon = tr.querySelector('.fa-chevron-down, .fa-chevron-up');
|
||||
if (!det) return;
|
||||
const open = det.style.display !== 'none';
|
||||
det.style.display = open ? 'none' : '';
|
||||
if (icon) icon.className = open ? 'fas fa-chevron-down text-muted' : 'fas fa-chevron-up text-muted';
|
||||
if (icon) icon.style.fontSize = '.65rem';
|
||||
}
|
||||
|
||||
function renderDashDetalle(t) {
|
||||
const e = s => escHtml(s || '—');
|
||||
const fmt = dt => dt ? new Date(dt.replace(' ','T')).toLocaleString('es-CO',{dateStyle:'short',timeStyle:'short'}) : '—';
|
||||
|
||||
const chips = [
|
||||
{ lbl:'Recepción (desk)', val: e(t.recepcion_desk_nombre) },
|
||||
{ lbl:'Atendió recepción', val: e(t.atendido_recepcion_nombre) },
|
||||
{ lbl:'Atendió muestras', val: e(t.atendido_lugar_nombre) },
|
||||
{ lbl:'Inicio recepción', val: fmt(t.inicio_recepcion_at) },
|
||||
{ lbl:'Fin recepción', val: fmt(t.fin_recepcion_at) },
|
||||
{ lbl:'Inicio servicio', val: fmt(t.inicio_lugar_at) },
|
||||
{ lbl:'Fin servicio', val: fmt(t.fin_lugar_at) },
|
||||
];
|
||||
|
||||
let grid = `<div style="display:grid;grid-template-columns:repeat(auto-fill,minmax(160px,1fr));gap:8px;margin-bottom:10px">
|
||||
${chips.map(c=>`<div style="background:#fff;border:1px solid #e2e8f0;border-radius:8px;padding:7px 11px">
|
||||
<div style="font-size:.63rem;text-transform:uppercase;letter-spacing:.07em;color:#94a3b8">${c.lbl}</div>
|
||||
<div style="font-size:.82rem;font-weight:600;color:#1e293b">${c.val}</div>
|
||||
</div>`).join('')}
|
||||
</div>`;
|
||||
|
||||
// Notas
|
||||
if (t.notas) {
|
||||
grid += `<div style="margin-bottom:8px;font-size:.82rem"><span style="font-size:.67rem;text-transform:uppercase;letter-spacing:.07em;color:#94a3b8">📝 Notas: </span>${e(t.notas)}</div>`;
|
||||
}
|
||||
|
||||
// Exámenes
|
||||
if (t.examenes && t.examenes.length) {
|
||||
grid += `<div style="margin-bottom:8px">
|
||||
<span style="font-size:.67rem;text-transform:uppercase;letter-spacing:.07em;color:#94a3b8"><i class="fas fa-vials me-1"></i>Exámenes (${t.examenes.length}): </span>
|
||||
${t.examenes.map(ex=>`<span class="badge" style="background:#dcfce7;color:#166534;margin:1px 2px">${e(ex.nombre)}</span>`).join('')}
|
||||
</div>`;
|
||||
}
|
||||
|
||||
// Comentarios
|
||||
if (t.comentarios && t.comentarios.length) {
|
||||
grid += `<div style="border-top:1px solid #e2e8f0;padding-top:8px">
|
||||
<div style="font-size:.67rem;text-transform:uppercase;letter-spacing:.07em;color:#94a3b8;margin-bottom:5px"><i class="fas fa-comments me-1"></i>Comentarios</div>
|
||||
${t.comentarios.map(c=>`<div style="font-size:.8rem;padding:4px 0;border-bottom:1px solid #f1f5f9">
|
||||
<strong>${e(c.usuario_nombre)}</strong>
|
||||
<span style="color:#94a3b8;font-size:.72rem;margin-left:6px">${fmt(c.creado_at)}</span>
|
||||
<div style="color:#475569">${e(c.comentario)}</div>
|
||||
</div>`).join('')}
|
||||
</div>`;
|
||||
}
|
||||
|
||||
return grid;
|
||||
}
|
||||
|
||||
function filtrarTabla() {
|
||||
const q = document.getElementById('filtroBusqueda').value.toLowerCase().trim();
|
||||
if (!q) { renderTabla(_turnos); return; }
|
||||
|
||||
@@ -129,7 +129,7 @@ $_hasVideo = (bool)$_tvVideo;
|
||||
margin-bottom: .2rem;
|
||||
}
|
||||
.turno-activo .codigo {
|
||||
font-size: clamp(5rem, 22vw, 16rem);
|
||||
font-size: clamp(4rem, 14vw, 10rem);
|
||||
font-weight: 900; line-height: 1;
|
||||
letter-spacing: -4px;
|
||||
color: var(--brand);
|
||||
@@ -160,13 +160,13 @@ $_hasVideo = (bool)$_tvVideo;
|
||||
100% { transform: translate(-50%, -50%) scale(.75); opacity: .15; }
|
||||
}
|
||||
.turno-activo .lugar-label {
|
||||
font-size: clamp(1.35rem, 3vw, 2.15rem);
|
||||
font-size: clamp(1.5rem, 3vw, 2.5rem);
|
||||
font-weight: 600;
|
||||
color: #94a3b8;
|
||||
margin-top: .3rem;
|
||||
}
|
||||
.turno-activo .lugar-nombre {
|
||||
font-size: clamp(1.75rem, 4vw, 3.05rem);
|
||||
font-size: clamp(3rem, 10vw, 8rem);
|
||||
font-weight: 800;
|
||||
color: #1e293b;
|
||||
margin-top: .1rem;
|
||||
|
||||
@@ -95,6 +95,12 @@ Layout::open('Historial de Turnos', 'fas fa-history');
|
||||
.detail-item .d-lbl { font-size:.67rem; text-transform:uppercase; letter-spacing:.07em; color:#94a3b8; }
|
||||
.detail-item .d-val { font-size:.85rem; font-weight:600; color:#1e293b; }
|
||||
|
||||
/* ── Exámenes y comentarios ── */
|
||||
.detail-extra { margin-top:10px; padding-top:10px; border-top:1px solid #e2e8f0; }
|
||||
.detail-extra .d-lbl { font-size:.67rem; text-transform:uppercase; letter-spacing:.07em; color:#94a3b8; margin-bottom:5px; }
|
||||
.detail-comment { padding:5px 0; border-bottom:1px solid #f1f5f9; font-size:.82rem; }
|
||||
.detail-comment:last-child { border-bottom:none; }
|
||||
|
||||
/* ── Documentos firmados ── */
|
||||
.docs-section { margin-top:10px; padding-top:10px; border-top:1px solid #e2e8f0; }
|
||||
.docs-lbl { font-size:.65rem; font-weight:700; text-transform:uppercase; letter-spacing:.07em; color:#94a3b8; margin-bottom:5px; }
|
||||
@@ -448,15 +454,42 @@ function renderDetalle(t) {
|
||||
return d.toLocaleString('es-CO',{dateStyle:'short',timeStyle:'short'});
|
||||
};
|
||||
const items = [
|
||||
{ lbl:'Creado', val: fmt(t.creado_at) },
|
||||
{ lbl:'Inicio recepción', val: fmt(t.inicio_recepcion_at) },
|
||||
{ lbl:'Fin recepción', val: fmt(t.fin_recepcion_at) },
|
||||
{ lbl:'Inicio servicio', val: fmt(t.inicio_lugar_at) },
|
||||
{ lbl:'Fin servicio', val: fmt(t.fin_lugar_at) },
|
||||
{ lbl:'Código completo', val: esc(t.codigo) },
|
||||
{ lbl:'Número en cola', val: t.numero ?? '—' },
|
||||
{ lbl:'Teléfono', val: esc(t.paciente_cel || '—') },
|
||||
{ lbl:'Creado', val: fmt(t.creado_at) },
|
||||
{ lbl:'Inicio recepción', val: fmt(t.inicio_recepcion_at) },
|
||||
{ lbl:'Fin recepción', val: fmt(t.fin_recepcion_at) },
|
||||
{ lbl:'Inicio servicio', val: fmt(t.inicio_lugar_at) },
|
||||
{ lbl:'Fin servicio', val: fmt(t.fin_lugar_at) },
|
||||
{ lbl:'Código completo', val: esc(t.codigo) },
|
||||
{ lbl:'Número en cola', val: t.numero ?? '—' },
|
||||
{ lbl:'Teléfono', val: esc(t.paciente_cel || '—') },
|
||||
{ lbl:'Recepción (desk)', val: esc(t.recepcion_desk_nombre || '—') },
|
||||
{ lbl:'Atendió recepción', val: esc(t.atendido_recepcion_nombre || '—') },
|
||||
{ lbl:'Atendió muestras', val: esc(t.atendido_lugar_nombre || '—') },
|
||||
];
|
||||
// Exámenes
|
||||
let examHtml = '';
|
||||
if (t.examenes && t.examenes.length) {
|
||||
examHtml = `<div class="detail-extra">
|
||||
<div class="d-lbl"><i class="fas fa-vials me-1 text-success"></i>Exámenes (${t.examenes.length})</div>
|
||||
<div class="d-val">${t.examenes.map(e => `<span class="badge bg-success-subtle text-success me-1 mb-1">${esc(e.nombre)}</span>`).join('')}</div>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
// Comentarios
|
||||
let comHtml = '';
|
||||
if (t.comentarios && t.comentarios.length) {
|
||||
comHtml = `<div class="detail-extra">
|
||||
<div class="d-lbl"><i class="fas fa-comments me-1 text-primary"></i>Comentarios</div>
|
||||
<div>${t.comentarios.map(c => `
|
||||
<div class="detail-comment">
|
||||
<span class="fw-semibold">${esc(c.usuario_nombre)}</span>
|
||||
<span class="text-muted small ms-1">${c.creado_at ? new Date(c.creado_at.replace(' ','T')).toLocaleString('es-CO',{dateStyle:'short',timeStyle:'short'}) : ''}</span>
|
||||
<div class="text-secondary">${esc(c.comentario)}</div>
|
||||
</div>`).join('')}
|
||||
</div>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
return `<div class="detail-grid">
|
||||
${items.map(it=>`
|
||||
<div class="detail-item">
|
||||
@@ -464,6 +497,7 @@ function renderDetalle(t) {
|
||||
<div class="d-val">${it.val}</div>
|
||||
</div>`).join('')}
|
||||
</div>
|
||||
${examHtml}${comHtml}
|
||||
<div class="docs-section" id="docs-${t.id}">
|
||||
<div class="docs-lbl"><i class="fas fa-file-signature me-1"></i>Documentos</div>
|
||||
<span class="text-muted small"><i class="fas fa-spinner fa-spin me-1"></i>Cargando…</span>
|
||||
|
||||
@@ -207,10 +207,10 @@ unset($p);
|
||||
.btn-prioridad:active { transform: scale(.95); filter: brightness(.93); }
|
||||
.btn-prioridad:hover { transform: translateY(-2px); box-shadow: 0 6px 20px rgba(0,0,0,.13); }
|
||||
.btn-prioridad .icon-wrap {
|
||||
width: 62px; height: 62px;
|
||||
border-radius: 14px;
|
||||
width: 90px; height: 90px;
|
||||
border-radius: 16px;
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
font-size: 2.04rem;
|
||||
font-size: 3rem;
|
||||
color: #fff;
|
||||
margin-bottom: .2rem;
|
||||
}
|
||||
@@ -265,7 +265,7 @@ unset($p);
|
||||
/* ── Print: ticket para impresora térmica SRP-B300 ── */
|
||||
@media print {
|
||||
@page { margin: 1mm 2mm; size: 80mm auto; }
|
||||
body { background: #fff !important; font-family: 'Courier New', Courier, 'Lucida Console', monospace !important; -webkit-print-color-adjust: exact !important; print-color-adjust: exact !important; }
|
||||
body { background: #fff !important; font-family: Arial, Helvetica, sans-serif !important; -webkit-print-color-adjust: exact !important; print-color-adjust: exact !important; -webkit-font-smoothing: antialiased !important; }
|
||||
.kiosko-topbar, .kiosko-footer, .spinner-overlay, .toast-error,
|
||||
.btn-nuevo, #screen-prio, #screen-datos { display: none !important; }
|
||||
#screen-ticket { display: block !important; opacity: 1 !important; position: static !important; }
|
||||
@@ -278,13 +278,13 @@ unset($p);
|
||||
border-top: none !important; border-radius: 0 !important;
|
||||
text-align: center !important;
|
||||
}
|
||||
.ticket-label { font-size: 8pt !important; letter-spacing: 0.03em !important; margin-bottom: 0 !important; padding-top: 2pt !important; }
|
||||
.ticket-label { font-size: 10pt !important; font-weight: 600 !important; letter-spacing: 0.03em !important; margin-bottom: 0 !important; padding-top: 2pt !important; }
|
||||
.ticket-codigo { font-size: 22pt !important; font-weight: 700 !important; letter-spacing: 0 !important; line-height: 1 !important; padding: 0 !important; }
|
||||
.ticket-prio-badge { font-size: 8pt !important; padding: 0 !important; margin: 0 !important; background: transparent !important; color: #000 !important; }
|
||||
.ticket-prio-badge { font-size: 10pt !important; font-weight: 600 !important; padding: 0 !important; margin: 0 !important; background: transparent !important; color: #000 !important; }
|
||||
.ticket-pos { margin: 2pt 0 !important; padding: 0 !important; border-radius: 0 !important; background: transparent !important; }
|
||||
.ticket-pos .lbl { font-size: 7pt !important; letter-spacing: 0.03em !important; }
|
||||
.ticket-pos .num { font-size: 11pt !important; font-weight: 700 !important; color: #000 !important; }
|
||||
.ticket-instruc { font-size: 7pt !important; margin: 2pt 0 !important; line-height: 1.15 !important; padding-bottom: 2pt !important; }
|
||||
.ticket-pos .lbl { font-size: 9pt !important; font-weight: 600 !important; letter-spacing: 0.03em !important; }
|
||||
.ticket-pos .num { font-size: 12pt !important; font-weight: 700 !important; color: #000 !important; }
|
||||
.ticket-instruc { font-size: 9pt !important; font-weight: 500 !important; margin: 2pt 0 !important; line-height: 1.3 !important; padding-bottom: 2pt !important; }
|
||||
.ticket-codigo, .ticket-label, .ticket-prio-badge,
|
||||
.ticket-pos, .ticket-pos .lbl, .ticket-pos .num,
|
||||
.ticket-instruc { color: #000 !important; }
|
||||
@@ -517,16 +517,20 @@ unset($p);
|
||||
inpCedula.focus();
|
||||
inpCedula.value = '';
|
||||
|
||||
// Detector de scanner: cuando se pega/escribe rápido una cédula válida
|
||||
// Detector de scanner: auto-submit solo si los dígitos llegan rápido (< 80ms entre cada uno)
|
||||
let _timerAutoSubmit = null;
|
||||
let _lastKeyAt = 0;
|
||||
inpCedula.addEventListener('input', () => {
|
||||
clearTimeout(_timerAutoSubmit);
|
||||
const now = Date.now();
|
||||
const gap = now - _lastKeyAt;
|
||||
_lastKeyAt = now;
|
||||
const val = inpCedula.value.trim();
|
||||
if (/^\d{5,15}$/.test(val)) {
|
||||
// Cédula válida: auto-submit en 300ms (tiempo para que termine el scanner)
|
||||
// Scanner envía dígitos en ráfaga; teclado humano siempre > 80ms entre teclas
|
||||
if (/^\d{5,15}$/.test(val) && gap < 80) {
|
||||
_timerAutoSubmit = setTimeout(() => confirmarTurno(), 300);
|
||||
}
|
||||
}, { once: false });
|
||||
});
|
||||
}
|
||||
|
||||
function volverPrioridades() { mostrar('screen-prio'); }
|
||||
|
||||
@@ -846,6 +846,14 @@ async function buscarPaciente() {
|
||||
|
||||
function seleccionarPaciente(pac) {
|
||||
pacienteActivo = pac;
|
||||
// Persistir vínculo en BD para que create_consent_token pueda verificar paciente_id
|
||||
if (turnoActivo?.id && pac?.id) {
|
||||
fetch(API + 'vincular_paciente.php', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ turno_id: turnoActivo.id, paciente_id: pac.id }),
|
||||
}).catch(() => {});
|
||||
}
|
||||
document.getElementById('bloque-pac-no-vinculado').classList.add('d-none');
|
||||
document.getElementById('bloque-pac-seleccionado').classList.remove('d-none');
|
||||
document.getElementById('lbl-pac-nombre').textContent =
|
||||
@@ -959,17 +967,6 @@ function renderHistorialTimeline(turnos, total) {
|
||||
</div>`;
|
||||
}
|
||||
|
||||
// Consentimientos
|
||||
let consStr = '';
|
||||
if (t.consentimientos && t.consentimientos.length) {
|
||||
consStr = `<div style="margin-top:6px;font-size:.75rem">
|
||||
<strong style="color:#64748b">Consentimientos:</strong>
|
||||
<div>${t.consentimientos.map(c =>
|
||||
`<span style="display:inline-block;margin-right:6px">${CONS_ICON[c.estado]||'?'} ${escHtml(c.nombre||'Consentimiento')}</span>`
|
||||
).join('')}</div>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
// Comentarios
|
||||
let comStr = '';
|
||||
if (t.comentarios && t.comentarios.length) {
|
||||
@@ -988,7 +985,7 @@ function renderHistorialTimeline(turnos, total) {
|
||||
<div class="timeline-codigo" style="color:${color}">${escHtml(t.codigo)}</div>
|
||||
<div class="timeline-lugar">${lugar}</div>
|
||||
<span class="timeline-eb" style="${eStyle}">${escHtml(eLbl)}</span>${mins}
|
||||
${examStr}${consStr}${comStr}
|
||||
${examStr}${comStr}
|
||||
</div>
|
||||
</div>`;
|
||||
}).join('');
|
||||
|
||||
Reference in New Issue
Block a user