Hasta ahora, para que el paciente firmara el consentimiento de bienvenida había que girarle el monitor a la recepcionista o pasarle el mouse: el botón Firmar abre el formulario en la pantalla de ella. Se agrega una pantalla aparte (/erp.php?m=turnero&v=firma) para poner frente al paciente. Sola, sin que la recepcionista haga nada: cuando el turno entra a ese puesto y le falta el F-LAB-01, aparece su nombre y un botón grande de firmar; al terminar dice gracias y vuelve a reposo. Reutiliza lo que ya existía: el formulario de ver_formulario_enviado.php, el aviso 'turneroFirmado' que ya emite al firmar, y el sondeo de recepción, que seguirá poniendo el renglón en verde sin cambios. Sobre el acceso: va en PUBLIC_ROUTES porque nadie va a iniciar sesión cada mañana en una tablet que manipula el público. No queda abierta: se identifica por la cookie del dispositivo, y sin ella no muestra ningún dato. El endpoint devuelve solo el turno que está en ese puesto en ese instante —no permite buscar, ni ver otros, ni consultar historial— y descarta turnos de días anteriores, igual que el televisor. La tablet debe quedar en modo kiosco. Falta registrar el dispositivo de Recepción 4 desde Configuración; hoy no tiene ninguno. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
201 lines
7.5 KiB
PHP
201 lines
7.5 KiB
PHP
<?php
|
|
/**
|
|
* modules/turnero/views/firma.php
|
|
* Tablet de firma del paciente — /erp.php?m=turnero&v=firma
|
|
*
|
|
* Pantalla de cara al público: se pone frente al paciente en el mostrador y
|
|
* muestra, sola, el consentimiento de bienvenida cuando le toca firmarlo.
|
|
* Antes había que girarle el monitor a la recepcionista o pasarle el mouse.
|
|
*
|
|
* La tablet se identifica por la cookie del dispositivo, registrada desde
|
|
* Configuración. Sin ella no muestra nada: es lo que impide que cualquiera
|
|
* abra esta dirección y vea el nombre del paciente de turno.
|
|
*
|
|
* Conviene dejarla en modo kiosco, sin barra de direcciones, para que desde
|
|
* ella no se pueda navegar al resto del ERP.
|
|
*/
|
|
|
|
$_fCfg = [];
|
|
try {
|
|
$__pdo = Database::getInstance()->getConnection();
|
|
$_fCfg = $__pdo->query(
|
|
"SELECT clave, valor FROM lab_config WHERE clave IN ('empresa_nombre','doc_logo_base64','doc_color')"
|
|
)->fetchAll(PDO::FETCH_KEY_PAIR) ?: [];
|
|
} catch (\Throwable $_) {}
|
|
|
|
$_fNombre = htmlspecialchars($_fCfg['empresa_nombre'] ?? 'Laboratorio');
|
|
$_fLogo = $_fCfg['doc_logo_base64'] ?? '';
|
|
$_fColor = preg_match('/^#[0-9a-fA-F]{3,8}$/', $_fCfg['doc_color'] ?? '') ? $_fCfg['doc_color'] : '#1565c0';
|
|
?>
|
|
<!DOCTYPE html>
|
|
<html lang="es">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
|
|
<title>Firma · <?= $_fNombre ?></title>
|
|
<style>
|
|
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
|
:root { --brand: <?= $_fColor ?>; }
|
|
html, body {
|
|
height: 100%; width: 100%; overflow: hidden;
|
|
font-family: 'Segoe UI', system-ui, sans-serif;
|
|
background: #f8fafc; color: #1e293b;
|
|
-webkit-user-select: none; user-select: none;
|
|
}
|
|
.pantalla {
|
|
height: 100vh; display: flex; flex-direction: column;
|
|
align-items: center; justify-content: center;
|
|
text-align: center; padding: 4vh 5vw; gap: 2.5vh;
|
|
}
|
|
.pantalla.oculta { display: none; }
|
|
.logo { max-height: 14vh; max-width: 50vw; object-fit: contain; }
|
|
.marca { font-size: clamp(1.2rem, 3vw, 2rem); font-weight: 700; color: #64748b; }
|
|
|
|
.saludo { font-size: clamp(1.4rem, 4vw, 2.6rem); font-weight: 600; color: #94a3b8; }
|
|
.etiqueta { font-size: clamp(.9rem, 2vw, 1.2rem); font-weight: 700;
|
|
letter-spacing: 3px; text-transform: uppercase; color: #94a3b8; }
|
|
.paciente { font-size: clamp(1.8rem, 5.5vw, 3.4rem); font-weight: 800; line-height: 1.15; }
|
|
.codigo { font-size: clamp(1.1rem, 2.6vw, 1.6rem); font-weight: 700; color: var(--brand); }
|
|
|
|
/* Botón deliberadamente enorme: lo va a tocar gente mayor, de pie y de afán */
|
|
.btn-firmar {
|
|
margin-top: 2vh;
|
|
background: var(--brand); color: #fff; border: 0;
|
|
border-radius: 18px; cursor: pointer;
|
|
font-family: inherit; font-weight: 800;
|
|
font-size: clamp(1.5rem, 4.5vw, 2.6rem);
|
|
padding: clamp(1rem, 3.5vh, 2.2rem) clamp(2.5rem, 12vw, 6rem);
|
|
box-shadow: 0 10px 30px rgba(0,0,0,.18);
|
|
transition: transform .12s ease, filter .12s ease;
|
|
}
|
|
.btn-firmar:active { transform: scale(.96); filter: brightness(.92); }
|
|
|
|
.ok-icono { font-size: clamp(3.5rem, 12vw, 7rem); color: #16a34a; line-height: 1; }
|
|
.ok-txt { font-size: clamp(1.4rem, 4vw, 2.4rem); font-weight: 700; color: #166534; }
|
|
|
|
.aviso { font-size: clamp(1rem, 2.4vw, 1.4rem); color: #94a3b8; max-width: 34ch; line-height: 1.5; }
|
|
|
|
/* El formulario ocupa toda la pantalla al abrirse */
|
|
#marco { position: fixed; inset: 0; z-index: 50; background: #fff; display: none; }
|
|
#marco.abierto { display: block; }
|
|
#marco iframe { width: 100%; height: 100%; border: 0; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
|
|
<!-- En reposo: nadie a quien pedirle firma -->
|
|
<div class="pantalla" id="p-reposo">
|
|
<?php if ($_fLogo): ?><img class="logo" src="<?= htmlspecialchars($_fLogo) ?>" alt=""><?php endif; ?>
|
|
<div class="marca"><?= $_fNombre ?></div>
|
|
<div class="saludo">Bienvenido</div>
|
|
</div>
|
|
|
|
<!-- Hay alguien y le falta firmar -->
|
|
<div class="pantalla oculta" id="p-firmar">
|
|
<div class="etiqueta">Turno <span id="f-codigo"></span></div>
|
|
<div class="paciente" id="f-paciente"></div>
|
|
<div class="aviso">Por favor lea y firme el consentimiento para continuar con su atención.</div>
|
|
<button class="btn-firmar" id="btn-firmar">Firmar</button>
|
|
</div>
|
|
|
|
<!-- Ya firmó -->
|
|
<div class="pantalla oculta" id="p-gracias">
|
|
<div class="ok-icono">✓</div>
|
|
<div class="ok-txt">¡Gracias!</div>
|
|
<div class="aviso">Su consentimiento quedó registrado. Puede continuar en el mostrador.</div>
|
|
</div>
|
|
|
|
<!-- La tablet no está registrada -->
|
|
<div class="pantalla oculta" id="p-sin-registro">
|
|
<div class="marca"><?= $_fNombre ?></div>
|
|
<div class="aviso">Esta tablet todavía no está asignada a un puesto.<br>
|
|
Regístrela desde Configuración del turnero.</div>
|
|
</div>
|
|
|
|
<div id="marco"><iframe id="marco-iframe" src="about:blank"></iframe></div>
|
|
|
|
<script>
|
|
const API = '<?= defined('BASE_URL') ? BASE_URL : '/' ?>modules/turnero/api/';
|
|
|
|
let turnoEnPantalla = null; // turno que se está mostrando
|
|
let firmando = false; // con el formulario abierto no se cambia de pantalla
|
|
|
|
function mostrar(id) {
|
|
['p-reposo','p-firmar','p-gracias','p-sin-registro']
|
|
.forEach(p => document.getElementById(p).classList.toggle('oculta', p !== id));
|
|
}
|
|
|
|
function abrirFormulario(url) {
|
|
firmando = true;
|
|
document.getElementById('marco-iframe').src = url;
|
|
document.getElementById('marco').classList.add('abierto');
|
|
}
|
|
|
|
function cerrarFormulario() {
|
|
firmando = false;
|
|
document.getElementById('marco').classList.remove('abierto');
|
|
document.getElementById('marco-iframe').src = 'about:blank';
|
|
}
|
|
|
|
document.getElementById('btn-firmar').addEventListener('click', () => {
|
|
const url = document.getElementById('btn-firmar').dataset.url;
|
|
if (url) abrirFormulario(url);
|
|
});
|
|
|
|
async function revisar() {
|
|
// Con el formulario abierto no se toca la pantalla: el paciente está firmando
|
|
if (firmando) return;
|
|
|
|
let d;
|
|
try {
|
|
const r = await fetch(API + 'get_firma_pendiente.php', { cache: 'no-store' });
|
|
d = await r.json();
|
|
} catch (_) {
|
|
return; // Sin red se deja lo que haya puesto; ya volverá
|
|
}
|
|
|
|
if (!d.ok) {
|
|
if (d.motivo === 'sin_dispositivo') mostrar('p-sin-registro');
|
|
return;
|
|
}
|
|
|
|
if (d.estado === 'por_firmar') {
|
|
document.getElementById('f-codigo').textContent = d.codigo || '';
|
|
document.getElementById('f-paciente').textContent = d.paciente || '';
|
|
document.getElementById('btn-firmar').dataset.url = d.url || '';
|
|
turnoEnPantalla = d.turno_id;
|
|
mostrar('p-firmar');
|
|
return;
|
|
}
|
|
|
|
if (d.estado === 'firmado') {
|
|
// El "gracias" solo tiene sentido para quien acaba de firmar aquí.
|
|
// Si la tablet se abre con un turno ya firmado de antes, va a reposo.
|
|
mostrar(turnoEnPantalla === d.turno_id ? 'p-gracias' : 'p-reposo');
|
|
return;
|
|
}
|
|
|
|
// reposo: se olvida el turno anterior para no dejar datos de un paciente
|
|
// en pantalla mientras llega el siguiente
|
|
turnoEnPantalla = null;
|
|
mostrar('p-reposo');
|
|
}
|
|
|
|
// ver_formulario_enviado.php avisa al terminar; si no llega el aviso, el sondeo
|
|
// se encarga igual cuando el consentimiento aparezca como firmado.
|
|
window.addEventListener('message', (e) => {
|
|
const t = e.data && e.data.type;
|
|
if (t === 'turneroFirmado') {
|
|
cerrarFormulario();
|
|
mostrar('p-gracias');
|
|
setTimeout(revisar, 2500);
|
|
}
|
|
});
|
|
|
|
revisar();
|
|
setInterval(revisar, 2000);
|
|
</script>
|
|
|
|
</body>
|
|
</html>
|