|
|
|
@@ -197,6 +197,31 @@ $adminNombre = $_SESSION['admin_user']['full_name'] ?? $_SESSION['admin_user']['
|
|
|
|
|
border: 1px solid #bfdbfe; border-radius: 8px;
|
|
|
|
|
padding: .25rem .75rem; font-size: .82rem; font-weight: 700;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ── Toast ── */
|
|
|
|
|
.rec-toast {
|
|
|
|
|
position: fixed; bottom: 2rem; right: 2rem; z-index: 9999;
|
|
|
|
|
display: flex; align-items: center; gap: .7rem;
|
|
|
|
|
padding: .8rem 1.2rem; border-radius: 12px;
|
|
|
|
|
background: #fff; border: 1px solid #e2e8f0;
|
|
|
|
|
box-shadow: 0 8px 30px rgba(0,0,0,.12);
|
|
|
|
|
font-size: .9rem; font-weight: 500;
|
|
|
|
|
transform: translateY(120%); opacity: 0;
|
|
|
|
|
transition: transform .3s ease, opacity .3s ease;
|
|
|
|
|
pointer-events: none;
|
|
|
|
|
}
|
|
|
|
|
.rec-toast.show {
|
|
|
|
|
transform: translateY(0); opacity: 1;
|
|
|
|
|
}
|
|
|
|
|
.rec-toast.success { border-left: 4px solid #22c55e; }
|
|
|
|
|
.rec-toast.info { border-left: 4px solid #3b82f6; }
|
|
|
|
|
.rec-toast.warn { border-left: 4px solid #f59e0b; }
|
|
|
|
|
.rec-toast.error { border-left: 4px solid #ef4444; }
|
|
|
|
|
.rec-toast .ico { font-size: 1.2rem; }
|
|
|
|
|
.rec-toast.success .ico { color: #22c55e; }
|
|
|
|
|
.rec-toast.info .ico { color: #3b82f6; }
|
|
|
|
|
.rec-toast.warn .ico { color: #f59e0b; }
|
|
|
|
|
.rec-toast.error .ico { color: #ef4444; }
|
|
|
|
|
</style>
|
|
|
|
|
</head>
|
|
|
|
|
<body>
|
|
|
|
@@ -404,6 +429,11 @@ require_once __DIR__ . '/../../../shared/components/sidebar.php';
|
|
|
|
|
</div><!-- /rec-layout -->
|
|
|
|
|
</main>
|
|
|
|
|
|
|
|
|
|
<div class="rec-toast" id="rec-toast">
|
|
|
|
|
<span class="ico" id="toast-ico"><i class="fas fa-check-circle"></i></span>
|
|
|
|
|
<span id="toast-msg"></span>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- ══ Modal firma presencial ═════════════════════════════════ -->
|
|
|
|
|
<div class="modal-firma-backdrop" id="modal-firma-rec">
|
|
|
|
|
<div class="modal-firma-box">
|
|
|
|
@@ -432,7 +462,7 @@ const DESK_ID = <?= $deskId ?: 'null' ?>;
|
|
|
|
|
// ── Arranque ──────────────────────────────────────────────────
|
|
|
|
|
document.addEventListener('DOMContentLoaded', () => {
|
|
|
|
|
cargarCola();
|
|
|
|
|
pollingColaId = setInterval(cargarCola, 8000);
|
|
|
|
|
pollingColaId = setInterval(cargarCola, 3000);
|
|
|
|
|
document.getElementById('inp-buscar-pac')
|
|
|
|
|
.addEventListener('keydown', e => { if (e.key === 'Enter') buscarPaciente(); });
|
|
|
|
|
});
|
|
|
|
@@ -460,7 +490,7 @@ function renderCola(snap) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
lista.innerHTML = espera.map(t => `
|
|
|
|
|
<div class="cola-card ${turnoActivo?.id === t.id ? 'activo' : ''}">
|
|
|
|
|
<div class="cola-card ${turnoActivo?.id === t.id ? 'activo' : ''}" data-id="${t.id}">
|
|
|
|
|
<div class="prio-dot" style="background:${t.prioridad_color};cursor:pointer" onclick="seleccionarTurno(${t.id})">${t.prioridad_codigo}</div>
|
|
|
|
|
<div class="turno-info" style="cursor:pointer" onclick="seleccionarTurno(${t.id})">
|
|
|
|
|
<div class="cod">${escHtml(t.codigo)}</div>
|
|
|
|
@@ -479,6 +509,7 @@ function renderCola(snap) {
|
|
|
|
|
async function llamarSiguiente() {
|
|
|
|
|
const btn = document.getElementById('btn-llamar');
|
|
|
|
|
btn.disabled = true;
|
|
|
|
|
mostrarToast('Consultando siguiente turno…', 'info', 1500);
|
|
|
|
|
try {
|
|
|
|
|
const res = await fetch(API + 'llamar_turno.php', {
|
|
|
|
|
method: 'POST',
|
|
|
|
@@ -491,8 +522,10 @@ async function llamarSiguiente() {
|
|
|
|
|
const turnoLlamado = json.turno ?? json.data?.turno;
|
|
|
|
|
if (!turnoLlamado) {
|
|
|
|
|
mostrarAviso('Cola vacía', 'No hay turnos en espera.');
|
|
|
|
|
resetPlaceholder();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
mostrarLlamando(turnoLlamado.codigo);
|
|
|
|
|
abrirFicha(turnoLlamado);
|
|
|
|
|
cargarCola();
|
|
|
|
|
} catch (err) {
|
|
|
|
@@ -516,6 +549,12 @@ async function seleccionarTurno(turnoId) {
|
|
|
|
|
|
|
|
|
|
// ── Llamar turno específico (cambia estado → en_recepcion) ─────────
|
|
|
|
|
async function llamarTurnoEspecifico(turnoId, estadoActual) {
|
|
|
|
|
// Mostrar feedback inmediato
|
|
|
|
|
const card = document.querySelector(`.cola-card[data-id="${turnoId}"]`);
|
|
|
|
|
if (card) {
|
|
|
|
|
const cod = card.querySelector('.cod')?.textContent || '';
|
|
|
|
|
mostrarLlamando(cod);
|
|
|
|
|
}
|
|
|
|
|
// Si ya está en recepción: re-llamar (actualiza llamado_at para que la pantalla TV reactive)
|
|
|
|
|
if (estadoActual === 'en_recepcion') {
|
|
|
|
|
try {
|
|
|
|
@@ -860,22 +899,20 @@ async function pasarALugar() {
|
|
|
|
|
solicitudActiva = null;
|
|
|
|
|
document.getElementById('ficha-turno').classList.add('d-none');
|
|
|
|
|
document.getElementById('badge-turno-activo').classList.add('d-none');
|
|
|
|
|
cargarCola();
|
|
|
|
|
|
|
|
|
|
// Mostrar aviso con link al lugar
|
|
|
|
|
const ph = document.getElementById('ficha-placeholder');
|
|
|
|
|
ph.classList.remove('d-none');
|
|
|
|
|
ph.innerHTML = `
|
|
|
|
|
<i class="fas fa-check-circle fa-3x mb-3" style="color:#22c55e"></i>
|
|
|
|
|
<p class="fw-semibold mb-1">Turno pasado al lugar correctamente</p>
|
|
|
|
|
<small class="text-muted d-block mb-3">El paciente aparece en la cola del puesto de toma de muestras</small>
|
|
|
|
|
<a href="<?= BASE_URL ?>erp.php?m=turnero&v=lugar" target="_blank"
|
|
|
|
|
class="btn btn-primary btn-sm">
|
|
|
|
|
<i class="fas fa-flask me-1"></i>Ir a Vista Lugar
|
|
|
|
|
</a>
|
|
|
|
|
<button class="btn btn-outline-secondary btn-sm ms-2" onclick="resetPlaceholder()">
|
|
|
|
|
Siguiente turno
|
|
|
|
|
</button>`;
|
|
|
|
|
mostrarToast('Turno pasado al lugar. Llamando siguiente…', 'success', 2000);
|
|
|
|
|
|
|
|
|
|
// Llamar siguiente automáticamente después de 1.5 s
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
cargarCola();
|
|
|
|
|
resetPlaceholder();
|
|
|
|
|
// Auto-llamar siguiente turno si hay cola
|
|
|
|
|
document.getElementById('ficha-placeholder').classList.remove('d-none');
|
|
|
|
|
document.getElementById('ficha-placeholder').innerHTML = `
|
|
|
|
|
<i class="fas fa-hourglass-half fa-3x mb-3" style="color:#3b82f6"></i>
|
|
|
|
|
<p class="fw-semibold mb-0">Llamando siguiente turno…</p>`;
|
|
|
|
|
llamarSiguiente();
|
|
|
|
|
}, 1500);
|
|
|
|
|
|
|
|
|
|
} catch (err) {
|
|
|
|
|
mostrarError(err.message);
|
|
|
|
@@ -925,10 +962,28 @@ function resetPlaceholder() {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function mostrarError(msg) {
|
|
|
|
|
alert('Error: ' + msg);
|
|
|
|
|
mostrarToast(msg, 'error', 4000);
|
|
|
|
|
}
|
|
|
|
|
function mostrarAviso(titulo, msg) {
|
|
|
|
|
alert(titulo + '\n' + msg);
|
|
|
|
|
mostrarToast(titulo + ': ' + msg, 'warn', 3500);
|
|
|
|
|
}
|
|
|
|
|
function mostrarLlamando(codigo) {
|
|
|
|
|
mostrarToast('Llamando turno ' + codigo + '…', 'info', 2000);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ── Toast ────────────────────────────────────────────────────
|
|
|
|
|
let toastTimer = null;
|
|
|
|
|
function mostrarToast(msg, type = 'info', duration = 2500) {
|
|
|
|
|
const el = document.getElementById('rec-toast');
|
|
|
|
|
const ico = document.getElementById('toast-ico');
|
|
|
|
|
const txt = document.getElementById('toast-msg');
|
|
|
|
|
const icons = { success: 'fa-check-circle', info: 'fa-info-circle', warn: 'fa-exclamation-triangle', error: 'fa-times-circle' };
|
|
|
|
|
el.className = 'rec-toast ' + type;
|
|
|
|
|
ico.innerHTML = '<i class="fas ' + (icons[type] || icons.info) + '"></i>';
|
|
|
|
|
txt.textContent = msg;
|
|
|
|
|
el.classList.add('show');
|
|
|
|
|
clearTimeout(toastTimer);
|
|
|
|
|
toastTimer = setTimeout(() => el.classList.remove('show'), duration);
|
|
|
|
|
}
|
|
|
|
|
</script>
|
|
|
|
|
</body>
|
|
|
|
|