getConnection(); // ── Cargar datos para los tabs (con fallback para tablas aún no migradas) ── $lugares = []; $prioridades = []; $examTipos = []; $formulariosCons= []; $sesiones = []; $sesionHoy = false; $cfg = []; $_loadErrors = []; try { $lugares = $pdo->query( 'SELECT * FROM turnero_lugares ORDER BY sort_order ASC, id ASC' )->fetchAll(PDO::FETCH_ASSOC); // Cargar formularios de consentimiento vinculados a cada lugar $lugarFormIds = []; foreach ($lugares as $lu) { $stmtLcf = $pdo->prepare( "SELECT formulario_id FROM turnero_lugar_consentimientos WHERE lugar_id = ?" ); $stmtLcf->execute([$lu['id']]); $lugarFormIds[(int)$lu['id']] = $stmtLcf->fetchAll(PDO::FETCH_COLUMN); } } catch (\Throwable $e) { $_loadErrors[] = 'turnero_lugares: ' . $e->getMessage(); $lugarFormIds = []; } try { $prioridades = $pdo->query( 'SELECT * FROM turnero_prioridades ORDER BY orden_peso ASC' )->fetchAll(PDO::FETCH_ASSOC); } catch (\Throwable $e) { $_loadErrors[] = 'turnero_prioridades: ' . $e->getMessage(); } try { $examTipos = $pdo->query( 'SELECT et.*, GROUP_CONCAT(etc.formulario_id) AS form_ids FROM exam_tipos et LEFT JOIN exam_tipo_consentimientos etc ON etc.exam_tipo_id = et.id GROUP BY et.id ORDER BY et.categoria ASC, et.nombre ASC' )->fetchAll(PDO::FETCH_ASSOC); } catch (\Throwable $e) { $_loadErrors[] = 'exam_tipos: ' . $e->getMessage(); } try { $formulariosCons = $pdo->query( "SELECT id, nombre FROM lab_formularios WHERE is_active=1 ORDER BY nombre ASC" )->fetchAll(PDO::FETCH_ASSOC); } catch (\Throwable $e) { $_loadErrors[] = 'lab_formularios: ' . $e->getMessage(); } try { $sesiones = $pdo->query( 'SELECT s.*, a1.full_name AS abierto_nombre, a2.full_name AS cerrado_nombre FROM turnero_sesiones s LEFT JOIN admin_users a1 ON a1.id = s.abierto_por LEFT JOIN admin_users a2 ON a2.id = s.cerrado_por ORDER BY s.fecha DESC LIMIT 30' )->fetchAll(PDO::FETCH_ASSOC); } catch (\Throwable $e) { $_loadErrors[] = 'turnero_sesiones: ' . $e->getMessage(); } try { $stmtHoy = $pdo->prepare( "SELECT s.*, (SELECT COUNT(*) FROM turnero_turnos WHERE sesion_id = s.id) AS total_turnos FROM turnero_sesiones s WHERE s.fecha = CURDATE()" ); $stmtHoy->execute(); $sesionHoy = $stmtHoy->fetch(PDO::FETCH_ASSOC); } catch (\Throwable $e) { $_loadErrors[] = 'sesion_hoy: ' . $e->getMessage(); } try { $cfgRows = $pdo->query("SELECT clave, valor FROM lab_config WHERE clave LIKE 'turnero_%'")->fetchAll(PDO::FETCH_ASSOC); foreach ($cfgRows as $r) { $cfg[$r['clave']] = $r['valor']; } } catch (\Throwable $e) { $_loadErrors[] = 'lab_config: ' . $e->getMessage(); } $tab = $_GET['tab'] ?? 'lugares'; ?>
= htmlspecialchars($le) ?>Escritorios de Recepción
= count($lugaresRec) ?>Cada escritorio es una ventanilla donde un recepcionista atiende pacientes. Aparecerá en la columna izquierda de la pantalla TV global.
No hay escritorios de recepción configurados
Agregar escritorio de recepción
Estaciones de Toma de Muestras
= count($lugaresMues) ?>Cada estación es donde se toman muestras o se realizan pruebas. Aparecerá en la columna derecha de la pantalla TV global.
No hay estaciones de muestras configuradas
Agregar estación de toma de muestras
URLs de pantalla TV
Muestra TODOS los escritorios de recepción y estaciones de muestras en una sola pantalla.
Tipos de examen y sus formularios de consentimiento
$items): ?>No hay tipos de examen configurados
Agregar tipo de examen
Vista inversa: por consentimiento
'.$e['codigo'].'', $exsVinculados))
?>
Códigos de prioridad (menor orden_peso = mayor prioridad)
Los códigos de prioridad no se pueden eliminar (están vinculados a turnos). Solo puede editarlos.
Sesión de hoy
No hay sesión creada para hoy.
Historial de sesiones (últimas 30)
Sin historial
| Fecha | Inicio | Cierre | Abierto por | Estado |
|---|---|---|---|---|
| = htmlspecialchars($s['fecha']) ?> | = $s['inicio_at'] ? date('H:i', strtotime($s['inicio_at'])) : '—' ?> | = $s['fin_at'] ? date('H:i', strtotime($s['fin_at'])) : '—' ?> | = htmlspecialchars($s['abierto_nombre'] ?? '—') ?> | = $s['fin_at'] ? 'Cerrada' : 'Abierta' ?> |
Notificaciones WhatsApp
{{1}}) para el enlace de firma.
El sistema usará texto plano como fallback si la plantilla falla.
Video de fondo / publicidad
El video se reproduce en bucle y sin sonido como fondo en la pantalla TV del turnero. Formatos admitidos: MP4, WebM. Máximo 500 MB.
Video actual: