This commit is contained in:
Lizandro Guarnizo
2026-04-21 16:03:42 -05:00
parent 8165f66ac0
commit abd28b730a
4 changed files with 75 additions and 46 deletions
+35 -16
View File
@@ -245,9 +245,7 @@ $_labColor = preg_match('/^#[0-9a-fA-F]{3,8}$/', $_dispCfg['doc_color'] ?? '')
</div>
</div>
<div class="header-right">
<span id="chip-estado" class="estado-chip conectando">
<i class="fas fa-circle-notch fa-spin"></i>Conectando…
</span>
<span id="chip-estado" class="estado-chip" style="display:none"></span>
<button id="btn-activar-sonido" onclick="activarSonido()" title="Activar sonido">
<i class="fas fa-volume-mute"></i> Sonido
</button>
@@ -309,7 +307,7 @@ $_labColor = preg_match('/^#[0-9a-fA-F]{3,8}$/', $_dispCfg['doc_color'] ?? '')
</div>
<script>
// ── Configuración desde URL ───────────────────────────────────
// ── Config desde URL ─────────────────────────────────────────
const params = new URLSearchParams(location.search);
const area = params.get('display') || 'recepcion';
const lugarId = params.get('lugar_id') || '';
@@ -319,7 +317,29 @@ const BASE_API = '<?= BASE_URL ?>modules/turnero/api/';
document.getElementById('lbl-area').textContent =
area === 'lugar' && lugarId ? `Lugar #${lugarId} — Turnos` : 'Recepción — Turnos';
// ── Reloj ─────────────────────────────────────────────────────
// ── Video publicitario desde URL ?video=URL ───────────────────
(function initVideo() {
const videoUrl = params.get('video');
if (!videoUrl) return;
const panel = document.getElementById('video-panel');
const src = document.getElementById('video-src');
const vid = document.getElementById('video-pub');
const body = document.getElementById('display-body');
src.src = videoUrl;
vid.load();
panel.style.display = 'block';
// 3 columnas: turno | video | cola
body.style.gridTemplateColumns = '1fr 320px 280px';
})();
// ── Helper: oscurecer color hex ───────────────────────────────
function shadeColor(hex, pct) {
const num = parseInt(hex.replace('#',''), 16);
const r = Math.max(0, Math.min(255, (num >> 16) + pct));
const g = Math.max(0, Math.min(255, ((num >> 8) & 0xff) + pct));
const b = Math.max(0, Math.min(255, (num & 0xff) + pct));
return `rgb(${r},${g},${b})`;
}
function actualizarReloj() {
document.getElementById('reloj').textContent =
new Date().toLocaleTimeString('es-CO', { hour: '2-digit', minute: '2-digit', second: '2-digit' });
@@ -381,17 +401,14 @@ function playBeep() {
function anunciarTurno(codigo, nombre) {
// 1. Beep electrónico
playBeep();
// 2. Voz sintética — funciona sin interacción previa en la mayoría de navegadores
// 2. Voz sintética — solo el número del turno, sin el nombre
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);
const utt = new SpeechSynthesisUtterance(`Turno ${letras}`);
utt.lang = 'es-CO';
utt.rate = 0.88;
utt.rate = 0.85;
utt.pitch = 1.05;
utt.volume = 1;
window.speechSynthesis.speak(utt);
@@ -425,7 +442,9 @@ function renderSnapshot(snap) {
}
elCodigo.textContent = activo.codigo;
elCodigo.style.color = activo.prioridad_color || '';
elCodigo.style.color = activo.prioridad_color
? shadeColor(activo.prioridad_color, -30)
: 'var(--brand-dark)';
elNombre.textContent = activo.paciente_nombre || '';
elPrio.style.background = (activo.prioridad_color || '#1565c0') + '22';
elPrio.style.color = activo.prioridad_color || '#1565c0';
@@ -520,8 +539,8 @@ function conectarSSE() {
esSource.addEventListener('open', () => {
reconnectDelay = 2000;
sseViva = true;
chipEstado.className = 'estado-chip conectado';
chipEstado.innerHTML = '<i class="fas fa-circle me-1" style="font-size:.5rem"></i>En vivo';
// SSE conectado → ocultar chip (el polling garantiza actualización)
chipEstado.style.display = 'none';
});
esSource.addEventListener('cola_update', (e) => {
@@ -533,8 +552,8 @@ function conectarSSE() {
esSource.addEventListener('error', () => {
sseViva = false;
chipEstado.className = 'estado-chip error';
chipEstado.innerHTML = '<i class="fas fa-exclamation-circle me-1"></i>Reconectando…';
// Solo mostramos chip cuando SSE falla (el polling sigue funcionando)
chipEstado.style.display = 'none'; // silencioso — polling cubre
esSource.close();
setTimeout(conectarSSE, reconnectDelay);
reconnectDelay = Math.min(reconnectDelay * 2, 30000);