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'; ?> Configuración Turnero
Algunas tablas aún no existen (ejecuta las migraciones pendientes):
($l['tipo'] ?? 'muestras') === 'recepcion')); $lugaresMues = array_values(array_filter($lugares, fn($l) => ($l['tipo'] ?? 'muestras') === 'muestras')); ?>

Escritorios de Recepción

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

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

Pantalla Global (sala de espera — recomendada)

Muestra TODOS los escritorios de recepción y estaciones de muestras en una sola pantalla.

Pantallas individuales por estación de muestras
Kiosko de turnos (tablet entrada)

Tipos de examen y sus formularios de consentimiento

$items): ?>
Inactivo

No hay tipos de examen configurados

Agregar tipo de examen

Vista inversa: por consentimiento

Sin exámenes vinculados' : implode(', ', array_map(fn($e)=>''.$e['codigo'].'', $exsVinculados)) ?>

Códigos de prioridad (menor orden_peso = mayor prioridad)

Orden:

Los códigos de prioridad no se pueden eliminar (están vinculados a turnos). Solo puede editarlos.

Sesión de hoy

Fecha: • Turnos: • Inicio: • Cierre:

No hay sesión creada para hoy.

Historial de sesiones (últimas 30)

Sin historial

Fecha Inicio Cierre Abierto por Estado

Notificaciones WhatsApp

>
>
Nombre exacto en Meta Business Manager
La plantilla debe estar aprobada en Meta y contener una variable de URL ({{1}}) para el enlace de firma del consentimiento. 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:

>