up
This commit is contained in:
@@ -107,16 +107,15 @@ try {
|
||||
$stmtItem->execute([$solicitudId, $examId]);
|
||||
}
|
||||
|
||||
// ── Calcular consentimientos necesarios (sin crearlos todavía) ──
|
||||
// Deduplica: varios exámenes que apuntan al mismo formulario → 1 solo ítem.
|
||||
$in = implode(',', array_fill(0, count($examIds), '?'));
|
||||
$stmt = $pdo->prepare(
|
||||
"SELECT DISTINCT etc.formulario_id, f.nombre AS formulario_nombre
|
||||
FROM exam_tipo_consentimientos etc
|
||||
JOIN lab_formularios f ON f.id = etc.formulario_id
|
||||
WHERE etc.exam_tipo_id IN ({$in})"
|
||||
// ── Consentimientos: todos los formularios activos del laboratorio ──
|
||||
// (no solo los vinculados a exámenes — el recepcionista elige cuáles aplican)
|
||||
$stmt = $pdo->prepare(
|
||||
"SELECT id AS formulario_id, nombre AS formulario_nombre
|
||||
FROM lab_formularios
|
||||
WHERE is_active = 1
|
||||
ORDER BY nombre ASC"
|
||||
);
|
||||
$stmt->execute($examIds);
|
||||
$stmt->execute();
|
||||
$consentimientosRequeridos = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
// Agregar estado actual de cada consentimiento (si ya fue enviado antes)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -229,23 +229,29 @@ unset($p);
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ══ PASO 2: Datos opcionales ════════════════════════════════ -->
|
||||
<!-- ══ PASO 2: Datos del paciente ═══════════════════════════════ -->
|
||||
<div id="screen-datos" class="screen hidden">
|
||||
<div class="kiosko-header">
|
||||
<div id="badge-prio" style="display:inline-block; padding:.5rem 1.4rem; border-radius:40px; font-size:1.2rem; font-weight:700; margin-bottom:1rem;"></div>
|
||||
<h1>Datos de contacto</h1>
|
||||
<p>Opcional — para notificaciones por WhatsApp</p>
|
||||
<h1>Ingresa tu cédula</h1>
|
||||
<p>Escribe tu número de documento de identidad</p>
|
||||
</div>
|
||||
|
||||
<div class="form-kiosko">
|
||||
<div class="mb-4">
|
||||
<label for="inp-nombre">Nombre completo</label>
|
||||
<input type="text" id="inp-nombre" class="form-control" placeholder="Ej: Juan Pérez" maxlength="120" autocomplete="off">
|
||||
<label for="inp-cedula">Número de cédula <span style="color:#ef4444">*</span></label>
|
||||
<input type="text" id="inp-cedula" class="form-control"
|
||||
placeholder="Ej: 1234567890" maxlength="20"
|
||||
autocomplete="off" inputmode="numeric">
|
||||
</div>
|
||||
<div class="mb-4">
|
||||
<label for="inp-cel">Celular (WhatsApp)</label>
|
||||
<input type="tel" id="inp-cel" class="form-control" placeholder="Ej: 3001234567" maxlength="20" autocomplete="off" inputmode="numeric">
|
||||
<div class="hint">Incluya código de país si es diferente a Colombia (+57)</div>
|
||||
<label for="inp-cel">WhatsApp
|
||||
<span style="color:#64748b;font-size:.85rem;font-weight:400"> (opcional)</span>
|
||||
</label>
|
||||
<input type="tel" id="inp-cel" class="form-control"
|
||||
placeholder="Ej: 3001234567" maxlength="20"
|
||||
autocomplete="off" inputmode="numeric">
|
||||
<div class="hint">Para recibir notificaciones de su turno</div>
|
||||
</div>
|
||||
<button class="btn-kiosko-main" onclick="confirmarTurno()">
|
||||
<i class="fas fa-ticket-alt me-2"></i>Obtener mi turno
|
||||
@@ -307,7 +313,7 @@ unset($p);
|
||||
document.getElementById('badge-prio').style.background = prioColor;
|
||||
|
||||
mostrar('screen-datos');
|
||||
document.getElementById('inp-nombre').focus();
|
||||
document.getElementById('inp-cedula').focus();
|
||||
}
|
||||
|
||||
function volverPrioridades() {
|
||||
@@ -316,12 +322,19 @@ unset($p);
|
||||
|
||||
// ── Crear turno ───────────────────────────────────────────
|
||||
async function confirmarTurno() {
|
||||
const nombre = document.getElementById('inp-nombre').value.trim();
|
||||
const cedula = document.getElementById('inp-cedula').value.trim();
|
||||
const cel = document.getElementById('inp-cel').value.trim();
|
||||
|
||||
// Validación mínima de celular
|
||||
if (!cedula) {
|
||||
mostrarError('Por favor ingresa tu número de cédula.');
|
||||
return;
|
||||
}
|
||||
if (!/^\d{5,15}$/.test(cedula)) {
|
||||
mostrarError('La cédula debe contener solo dígitos (mínimo 5).');
|
||||
return;
|
||||
}
|
||||
if (cel && !/^\+?\d{7,15}$/.test(cel.replace(/\s/g, ''))) {
|
||||
mostrarError('Ingrese un número de celular válido (solo dígitos).');
|
||||
mostrarError('Ingresa un número de WhatsApp válido.');
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -333,8 +346,8 @@ unset($p);
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
prioridad_codigo: prioCodigo,
|
||||
paciente_nombre: nombre || null,
|
||||
paciente_cel: cel || null,
|
||||
paciente_nombre: cedula, // se guarda como referencia de identificación
|
||||
paciente_cel: cel || null,
|
||||
}),
|
||||
});
|
||||
|
||||
@@ -372,7 +385,7 @@ unset($p);
|
||||
// ── Reinicio ──────────────────────────────────────────────
|
||||
function reiniciar() {
|
||||
clearTimeout(window._reinicioTimer);
|
||||
document.getElementById('inp-nombre').value = '';
|
||||
document.getElementById('inp-cedula').value = '';
|
||||
document.getElementById('inp-cel').value = '';
|
||||
prioCodigo = prioNombre = prioColor = '';
|
||||
mostrar('screen-prio');
|
||||
|
||||
@@ -602,12 +602,10 @@ async function guardarSolicitud() {
|
||||
|
||||
btn.innerHTML = '<i class="fas fa-check me-1"></i>Guardado';
|
||||
|
||||
// Mostrar sección de consentimientos si aplica
|
||||
if (consentimientos.length > 0) {
|
||||
renderConsentimientos(consentimientos);
|
||||
document.getElementById('sec-consentimientos').style.display = '';
|
||||
document.getElementById('btn-enviar-consent').classList.remove('d-none');
|
||||
}
|
||||
// Mostrar consentimientos (siempre — son todos los formularios del lab)
|
||||
renderConsentimientos(consentimientos);
|
||||
document.getElementById('sec-consentimientos').style.display = '';
|
||||
document.getElementById('btn-enviar-consent').classList.remove('d-none');
|
||||
|
||||
document.getElementById('btn-pasar-lugar').classList.remove('d-none');
|
||||
document.getElementById('btn-guardar').classList.add('d-none');
|
||||
|
||||
Reference in New Issue
Block a user