up
This commit is contained in:
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
/**
|
||||
* GET /modules/turnero/api/get_turno.php?id=TURNO_ID
|
||||
* Devuelve los datos completos de un turno para abrir su ficha en recepción.
|
||||
*/
|
||||
require_once __DIR__ . '/_helpers.php';
|
||||
requireMethod('GET');
|
||||
|
||||
$turnoId = isset($_GET['id']) ? (int) $_GET['id'] : 0;
|
||||
if (!$turnoId) {
|
||||
jsonError('id es obligatorio', 400);
|
||||
}
|
||||
|
||||
$pdo = db();
|
||||
|
||||
$stmt = $pdo->prepare(
|
||||
"SELECT t.*,
|
||||
p.codigo AS prioridad_codigo,
|
||||
p.nombre AS prioridad_nombre,
|
||||
p.color AS prioridad_color,
|
||||
p.orden_peso
|
||||
FROM turnero_turnos t
|
||||
JOIN turnero_prioridades p ON p.id = t.prioridad_id
|
||||
WHERE t.id = ?
|
||||
LIMIT 1"
|
||||
);
|
||||
$stmt->execute([$turnoId]);
|
||||
$turno = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
if (!$turno) {
|
||||
jsonError('Turno no encontrado', 404);
|
||||
}
|
||||
|
||||
jsonOk(['turno' => $turno]);
|
||||
@@ -187,6 +187,12 @@
|
||||
<span id="chip-estado" class="estado-chip conectando">
|
||||
<i class="fas fa-circle-notch fa-spin me-1"></i>Conectando…
|
||||
</span>
|
||||
<button id="btn-activar-sonido" onclick="activarSonido()" title="Toca para activar el sonido del turnero"
|
||||
style="background:rgba(59,130,246,0.15);border:1px solid rgba(59,130,246,0.4);color:#93c5fd;
|
||||
border-radius:8px;padding:6px 14px;font-size:.82rem;cursor:pointer;
|
||||
display:flex;align-items:center;gap:6px;white-space:nowrap">
|
||||
<i class="fas fa-volume-mute"></i> Activar sonido
|
||||
</button>
|
||||
<div class="reloj" id="reloj">--:--:--</div>
|
||||
</header>
|
||||
|
||||
@@ -259,28 +265,77 @@ function actualizarReloj() {
|
||||
actualizarReloj();
|
||||
setInterval(actualizarReloj, 1000);
|
||||
|
||||
// ── Audio (Web Audio API — sin archivos externos) ─────────────
|
||||
let audioCtx = null;
|
||||
function playBeep() {
|
||||
// ── Audio ─────────────────────────────────────────────────────
|
||||
let audioCtx = null;
|
||||
let sonidoActivo = false;
|
||||
|
||||
function activarSonido() {
|
||||
try {
|
||||
if (!audioCtx) audioCtx = new (window.AudioContext || window.webkitAudioContext)();
|
||||
const osc = audioCtx.createOscillator();
|
||||
const gain = audioCtx.createGain();
|
||||
osc.connect(gain);
|
||||
gain.connect(audioCtx.destination);
|
||||
osc.type = 'sine';
|
||||
osc.frequency.setValueAtTime(880, audioCtx.currentTime);
|
||||
osc.frequency.setValueAtTime(660, audioCtx.currentTime + 0.12);
|
||||
gain.gain.setValueAtTime(0.25, audioCtx.currentTime);
|
||||
gain.gain.exponentialRampToValueAtTime(0.0001, audioCtx.currentTime + 0.5);
|
||||
osc.start(audioCtx.currentTime);
|
||||
osc.stop(audioCtx.currentTime + 0.5);
|
||||
} catch (_) { /* Sin audio si el contexto no está disponible */ }
|
||||
audioCtx = new (window.AudioContext || window.webkitAudioContext)();
|
||||
// Beep silencioso para desbloquear el contexto en el mismo gesto
|
||||
const g = audioCtx.createGain();
|
||||
g.gain.setValueAtTime(0.001, audioCtx.currentTime);
|
||||
const o = audioCtx.createOscillator();
|
||||
o.connect(g); g.connect(audioCtx.destination);
|
||||
o.start(); o.stop(audioCtx.currentTime + 0.05);
|
||||
} catch (_) {}
|
||||
sonidoActivo = true;
|
||||
const btn = document.getElementById('btn-activar-sonido');
|
||||
if (btn) {
|
||||
btn.innerHTML = '<i class="fas fa-volume-up"></i> Sonido ON';
|
||||
btn.style.background = 'rgba(34,197,94,0.2)';
|
||||
btn.style.borderColor = 'rgba(34,197,94,0.5)';
|
||||
btn.style.color = '#86efac';
|
||||
btn.onclick = null;
|
||||
btn.style.cursor = 'default';
|
||||
// Reproducir beep de prueba al activar
|
||||
setTimeout(playBeep, 100);
|
||||
}
|
||||
}
|
||||
|
||||
// Desbloquear también si el usuario toca/clica cualquier parte de la pantalla
|
||||
document.addEventListener('click', () => { if (!sonidoActivo) activarSonido(); }, { once: true });
|
||||
document.addEventListener('touchstart', () => { if (!sonidoActivo) activarSonido(); }, { once: true });
|
||||
|
||||
function playBeep() {
|
||||
if (sonidoActivo && audioCtx) {
|
||||
try {
|
||||
const osc = audioCtx.createOscillator();
|
||||
const gain = audioCtx.createGain();
|
||||
osc.type = 'sine';
|
||||
osc.connect(gain);
|
||||
gain.connect(audioCtx.destination);
|
||||
osc.frequency.setValueAtTime(880, audioCtx.currentTime);
|
||||
osc.frequency.setValueAtTime(1100, audioCtx.currentTime + 0.1);
|
||||
osc.frequency.setValueAtTime(880, audioCtx.currentTime + 0.2);
|
||||
gain.gain.setValueAtTime(0.35, audioCtx.currentTime);
|
||||
gain.gain.exponentialRampToValueAtTime(0.0001, audioCtx.currentTime + 0.65);
|
||||
osc.start(audioCtx.currentTime);
|
||||
osc.stop(audioCtx.currentTime + 0.65);
|
||||
} catch (_) {}
|
||||
}
|
||||
}
|
||||
|
||||
function anunciarTurno(codigo, nombre) {
|
||||
// 1. Beep electrónico
|
||||
playBeep();
|
||||
// 2. Voz sintética — funciona sin interacción previa en la mayoría de navegadores
|
||||
if ('speechSynthesis' in window) {
|
||||
// Esperar a que el beep termine antes de hablar
|
||||
setTimeout(() => {
|
||||
window.speechSynthesis.cancel();
|
||||
const letras = codigo.split('').join(' '); // "A 0 1" para mejor pronunciación
|
||||
let texto = `Turno ${letras}.`;
|
||||
if (nombre) texto += ` ${nombre}, por favor pase a recepción.`;
|
||||
const utt = new SpeechSynthesisUtterance(texto);
|
||||
utt.lang = 'es-CO';
|
||||
utt.rate = 0.88;
|
||||
utt.pitch = 1.05;
|
||||
utt.volume = 1;
|
||||
window.speechSynthesis.speak(utt);
|
||||
}, 700);
|
||||
}
|
||||
}
|
||||
// Desbloquear AudioContext ante primer tap/clic (requerido por navegadores)
|
||||
document.addEventListener('click', () => {
|
||||
if (audioCtx && audioCtx.state === 'suspended') audioCtx.resume();
|
||||
}, { once: true });
|
||||
|
||||
// ── Estado previo ─────────────────────────────────────────────
|
||||
let lastCodigoActivo = null;
|
||||
@@ -302,7 +357,7 @@ function renderSnapshot(snap) {
|
||||
|
||||
// ¿Cambió el turno? → animar + sonido
|
||||
if (activo.codigo !== lastCodigoActivo) {
|
||||
playBeep();
|
||||
anunciarTurno(activo.codigo, activo.paciente_nombre || '');
|
||||
elRing.style.color = color;
|
||||
elRing.classList.remove('animar');
|
||||
// Forzar reflow para reiniciar animación
|
||||
|
||||
@@ -434,8 +434,16 @@ async function llamarSiguiente() {
|
||||
|
||||
// ── Seleccionar turno de la cola (para ver ficha de uno ya llamado) ─
|
||||
async function seleccionarTurno(turnoId) {
|
||||
// No abrir si ya hay uno en recepción (confuso)
|
||||
// No abrir si ya hay uno activo en recepción
|
||||
if (turnoActivo && turnoActivo.estado === 'en_recepcion') return;
|
||||
try {
|
||||
const res = await fetch(API + 'get_turno.php?id=' + turnoId);
|
||||
const json = await res.json();
|
||||
if (!json.ok) { mostrarError(json.error || 'No se pudo cargar el turno'); return; }
|
||||
abrirFicha(json.turno ?? json.data?.turno);
|
||||
} catch (err) {
|
||||
mostrarError('Error al cargar turno: ' + err.message);
|
||||
}
|
||||
}
|
||||
|
||||
// ── Abrir ficha ───────────────────────────────────────────────
|
||||
@@ -566,8 +574,8 @@ async function guardarSolicitud() {
|
||||
const json = await res.json();
|
||||
if (!json.ok) { mostrarError(json.error); return; }
|
||||
|
||||
solicitudActiva = json.data.solicitud;
|
||||
consentimientos = json.data.consentimientos_requeridos || [];
|
||||
solicitudActiva = json.solicitud ?? json.data?.solicitud;
|
||||
consentimientos = json.consentimientos_requeridos ?? json.data?.consentimientos_requeridos ?? [];
|
||||
|
||||
btn.innerHTML = '<i class="fas fa-check me-1"></i>Guardado';
|
||||
|
||||
@@ -619,7 +627,7 @@ async function enviarConsentimientos() {
|
||||
const json = await res.json();
|
||||
if (!json.ok) { mostrarError(json.error); return; }
|
||||
|
||||
consentimientos = json.data.consentimientos || consentimientos;
|
||||
consentimientos = json.consentimientos ?? json.data?.consentimientos ?? consentimientos;
|
||||
renderConsentimientos(consentimientos);
|
||||
btn.innerHTML = '<i class="fas fa-check me-1"></i>Enviado';
|
||||
} catch (err) {
|
||||
|
||||
Reference in New Issue
Block a user