feat(turnero): panel de estado de muestras en recepción
Recepción ve en una barra inferior qué turno atiende cada estación de toma de muestras (verde = ocupada, gris = libre), cuántos están en espera y cuántos en espera entre muestras progresivas. Se refresca cada 7 s. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
7b599b28ef
commit
7d444699d3
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
/**
|
||||
* GET get_resumen_muestras.php
|
||||
* Resumen de estaciones de toma de muestras para recepción.
|
||||
*/
|
||||
require_once __DIR__ . '/_helpers.php';
|
||||
requireTurnero();
|
||||
|
||||
$pdo = db();
|
||||
$sesionId = (int)(obtenerOCrearSesionHoy()['id'] ?? 0);
|
||||
|
||||
// Estaciones con el turno en servicio actual
|
||||
$stmt = $pdo->prepare(
|
||||
"SELECT l.id, l.nombre,
|
||||
t.id AS turno_id, t.codigo, t.paciente_nombre
|
||||
FROM turnero_lugares l
|
||||
LEFT JOIN turnero_turnos t
|
||||
ON t.lugar_destino_id = l.id
|
||||
AND t.sesion_id = ?
|
||||
AND t.estado = 'en_servicio'
|
||||
AND t.muestra_espera_at IS NULL
|
||||
AND t.fin_lugar_at IS NULL
|
||||
WHERE l.tipo = 'muestras' AND l.activo = 1
|
||||
ORDER BY l.sort_order"
|
||||
);
|
||||
$stmt->execute([$sesionId]);
|
||||
$estaciones = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
// Cola global de muestras (en_espera_lugar)
|
||||
$s = $pdo->prepare(
|
||||
"SELECT COUNT(*) FROM turnero_turnos
|
||||
WHERE sesion_id = ? AND estado = 'en_espera_lugar' AND muestra_espera_at IS NULL
|
||||
AND lugar_destino_id IN (SELECT id FROM turnero_lugares WHERE tipo='muestras' AND activo=1)"
|
||||
);
|
||||
$s->execute([$sesionId]);
|
||||
$enEspera = (int)$s->fetchColumn();
|
||||
|
||||
// Pacientes entre tomas (espera progresiva)
|
||||
$s2 = $pdo->prepare(
|
||||
"SELECT COUNT(*) FROM turnero_turnos
|
||||
WHERE sesion_id = ? AND muestra_espera_at IS NOT NULL AND fin_lugar_at IS NULL
|
||||
AND lugar_destino_id IN (SELECT id FROM turnero_lugares WHERE tipo='muestras' AND activo=1)"
|
||||
);
|
||||
$s2->execute([$sesionId]);
|
||||
$enEsperaMuestra = (int)$s2->fetchColumn();
|
||||
|
||||
jsonOk([
|
||||
'estaciones' => $estaciones,
|
||||
'en_espera' => $enEspera,
|
||||
'en_espera_muestra' => $enEsperaMuestra,
|
||||
]);
|
||||
@@ -934,6 +934,14 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
</div><!-- /rec-ficha -->
|
||||
|
||||
</div><!-- /rec-layout -->
|
||||
|
||||
<!-- ══ Panel resumen toma de muestras ════════════════════════ -->
|
||||
<div id="panel-muestras-resumen" style="padding:6px 12px;border-top:1px solid #e2e8f0;background:#f8fafc;display:flex;gap:8px;align-items:center;flex-wrap:wrap;min-height:38px">
|
||||
<span class="text-muted small fw-semibold me-1" style="white-space:nowrap"><i class="fas fa-vial me-1"></i>Muestras:</span>
|
||||
<div id="muestras-estaciones" class="d-flex gap-2 flex-wrap align-items-center"></div>
|
||||
<span id="muestras-cola-badge" class="badge bg-warning text-dark ms-auto d-none" title="En espera de ser llamados"></span>
|
||||
<span id="muestras-prog-badge" class="badge bg-info text-dark d-none" title="Esperando siguiente muestra (toma progresiva)"></span>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<!-- ═══ Modal firma enfermero ═══════════════════════════════════════ -->
|
||||
@@ -1200,6 +1208,8 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
|
||||
cargarCola();
|
||||
pollingColaId = setInterval(cargarCola, 3000);
|
||||
_cargarResumenMuestras();
|
||||
setInterval(_cargarResumenMuestras, 7000);
|
||||
let _pacBusqTimer;
|
||||
const _inpPac = document.getElementById('inp-buscar-pac');
|
||||
_inpPac.addEventListener('input', () => {
|
||||
@@ -3079,6 +3089,28 @@ async function mpacGuardar() {
|
||||
|
||||
// ── Toast ────────────────────────────────────────────────────
|
||||
let toastTimer = null;
|
||||
async function _cargarResumenMuestras() {
|
||||
try {
|
||||
const r = await fetch(API + 'get_resumen_muestras.php');
|
||||
const j = await r.json();
|
||||
if (!j.ok) return;
|
||||
const cont = document.getElementById('muestras-estaciones');
|
||||
if (!cont) return;
|
||||
cont.innerHTML = j.estaciones.map(e => {
|
||||
const libre = !e.turno_id;
|
||||
return `<span class="badge ${libre ? 'bg-secondary' : 'bg-success'}" style="font-size:.75rem;font-weight:500" title="${libre ? 'Libre' : escHtml(e.paciente_nombre || '')}">
|
||||
${escHtml(e.nombre)}: ${libre ? '—' : escHtml(e.codigo)}
|
||||
</span>`;
|
||||
}).join('');
|
||||
const cb = document.getElementById('muestras-cola-badge');
|
||||
cb.textContent = `${j.en_espera} en espera`;
|
||||
cb.classList.toggle('d-none', j.en_espera === 0);
|
||||
const pb = document.getElementById('muestras-prog-badge');
|
||||
pb.textContent = `${j.en_espera_muestra} prog.`;
|
||||
pb.classList.toggle('d-none', j.en_espera_muestra === 0);
|
||||
} catch {}
|
||||
}
|
||||
|
||||
function mostrarToast(msg, type = 'info', duration = 2500) {
|
||||
const el = document.getElementById('rec-toast');
|
||||
const ico = document.getElementById('toast-ico');
|
||||
|
||||
Reference in New Issue
Block a user