El timer de 1s solo escaneaba chips en la cola principal. Los pacientes
en espera entre extracciones (muestra_espera_at IS NOT NULL) viven en el
panel de espera sin chip, por lo que nunca sonaba. Se añade un chip oculto
con data-toma-at en cada fila del panel; el timer existente lo detecta y
dispara banner + sonido en todas las tablets del grupo.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Al llamar un paciente de toma progresiva desde la espera, ahora se
actualiza lugar_destino_id al puesto que llama, se pone llamado_lugar_at
para que el display anuncie al paciente, y se notifica por SSE. El JS
abre la ficha y muestra el toast igual que un llamar normal.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- llamar_turno.php: ambos SELECT de refresco hacen LEFT JOIN con
turnero_solicitudes para devolver COALESCE(ts.solo_muestras,0).
Sin este JOIN, JS recibía undefined y _esSoloEntrega() retornaba
false → polling de consentimientos arrancaba → hayPendientes=true
→ finalizar bloqueado (caso turno E047 ALVARO TIRADO).
- lugar.php renderConsentimientos: hayPendientes se fuerza a false
cuando _esSoloEntrega(turnoActivo) es true, para que un
consentimiento huérfano previo no bloquee la finalización en
turnos de solo entrega de muestras.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1. App::bacteDefaultUrl() ahora busca también por token cookie (antes
solo buscaba por IP), consistente con login.php y lugar.php.
2. cambiarEstadoTurno(): cuando el servidor devuelve 422 (transición
inválida porque otro operador cambió el estado), muestra mensaje
claro y refresca la UI en lugar de mostrar el error técnico bruto
"Transición 'en_recepcion' → 'finalizado' no está permitida".
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Permite al operador agregar un consentimiento informado extra (fuera de
los asignados por defecto) mientras el turno está en servicio.
- PHP: consulta lab_formularios activos al cargar lugar.php
- JS: modal de selección con lista de formularios
- Botón visible solo cuando turno está en_servicio, oculto en resetFicha
- API: modules/turnero/api/anexar_formulario.php crea el consentimiento
y notifica SSE para que el dispositivo lo vea inmediatamente
- Fix: _olvidarConsentimiento usaba cargarConsentimientos() inexistente;
corregido a actualizarConsentimientos(turnoActivo.id)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- feat(turnero): Botón "Olvidar" en lugar.php y recepcion.php para descartar firma de consentimiento completo (nuevo endpoint resetear_consentimiento.php)
- feat(turnero): Botón "Olvidar" por toma individual en prolongadas para borrar firma+hora de una toma específica (nuevo endpoint resetear_toma.php)
- feat(turnero): Editar tiempos en tomas en_progreso: hora_campo signed muestra readonly con botón olvidar, permite re-editar tras olvidar
- feat(turnero): Guardar tiempos (draft save) también en estado en_progreso
- feat(turnero): Observaciones de tomas prolongadas se muestran en header de tarjeta firmada (campo obs_campo en _mpMap)
- fix(turnero): Token del equipo se sobreescribía en tabla — ip permitía solo un registro con ip='' (UNIQUE); ahora ip es nullable
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- DB: añade estado 'cerrado_anticipado' al enum de turnero_consentimientos
- ver_formulario_enviado.php: POST handler mp_cerrar_anticipado guarda motivo
en _cierre_anticipado y cambia estado; botón "Cerrar anticipadamente" con modal
textarea; bloque display para estado cerrado_anticipado mostrando el motivo;
modoEditar y check de estado actualizados para excluir cerrado_anticipado
- get_consentimientos.php: expone cierre_anticipado en respuesta; mueve parse
de datos_respuestas fuera del if(esTomaProg) para ambos loops
- lugar.php: CONSENT_IC/LBL incluye cerrado_anticipado; ya[] lo incluye;
hayPendientes lo excluye; fila muestra motivo en banner amarillo
- recepcion.php: CONSENT_META incluye cerrado_anticipado; ya[] lo incluye;
motivo visible en la tarjeta del consentimiento
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- configurar_tomas.php: merge config por examen en lugar de overwrite
- ver_formulario_enviado.php: auto-abrir panel de tiempos al cargar; soporte formularios solo_profesional con botón Guardar y completar
- get_consentimientos.php: leer campo solo_profesional de lab_formularios
- recepcion.php: formularios sin firma paciente muestran badge "Completa en laboratorio"
- lugar.php: botón "Completar" para formularios solo_profesional
DB: ALTER TABLE lab_formularios ADD solo_profesional; UPDATE Rubeola id=8; UPDATE descripcion Niños kiosko
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- DB: columna muestra_espera_at en turnero_turnos
- Panel 'Esperando siguiente muestra' en la cola de lugar.php
· Muestra pacientes en espera con countdown a siguiente_toma_at
· Botón 'Llamar' los devuelve a la cola activa
- Botón 'Mandar a espera' en la ficha (visible cuando toma progresiva activa)
· Desaparece de la cola principal, queda en sección de espera
- get_cola.php: excluye muestra_espera_at IS NOT NULL de cola y activo
· Agrega array en_espera_muestra en la respuesta
- Nuevas APIs: poner_en_espera_muestra.php, llamar_desde_espera.php
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Sección en la parte superior de lugar.php muestra pacientes esperando
en Ginecología y Pediatría (inicio_lugar_at IS NOT NULL, fin_lugar_at IS NULL)
- Chips de acceso rápido (botones) al tope de la cola para navegar directo
- Contadores de espera actualizados cada 30s
- Clicking en un paciente o "Ir →" navega a esa vista completa para atenderlo
- Solo aparece si existen esos lugares en DB y hay pacientes esperando
- Nuevo: get_especialidades.php para consultar pacientes de lugares especiales
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Muestra que el paciente trae después no se muestra como rechazo.
Colores rojo → naranja, botón/etiqueta/íconos actualizados en lugar.php y bandeja.php.
DB/API internos sin cambio (enum 'rechazada' se mantiene).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Sonido más fuerte (gain 1.0, 3 pulsos grave-agudo), se repite cada
30 s mientras el banner no se cierre con click.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- cambiar_estado: permite en_espera_lugar→en_recepcion y en_servicio→en_recepcion
- Al volver de un lugar limpia lugar_destino_id, llamado_lugar_at, inicio_lugar_at,
fin_recepcion_at y atendido_lugar_por para que quede reasignable
- lugar.php: botón "A recepción" visible en en_espera_lugar y en_servicio
- resetFicha oculta el botón nuevo al limpiar la ficha
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- cancelar_toma_pendiente: añade JOIN por paciente_id para prevenir IDOR
- cancelar_toma_pendiente: requiere turno_id para validar propiedad
- get_tomas_pendientes: porta filtros condicionales y _tomas_config de
guardar_toma.php para mostrar conteo de firmas correcto
- lugar.php: refresca sección tomas previas al completar via turneroFirmado
- lugar.php: muestra error en catch de cargarTomasPrevias en vez de silenciar
- lugar.php: limpia _tomaAlertados al cancelar toma previa
- lugar.php: pasa turno_id en POST de cancelar para ownership check
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Cuando el paciente regresa con un turno nuevo, toma de muestras muestra
las tomas en_progreso de visitas anteriores con botón Continuar o Cancelar.
Cancelar marca estado='rechazado' (campo ya existía en el enum).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
El iframe pasa targetMs al padre via postMessage. El padre corre su propio
intervalo (5s) que dispara la notificación si el modal está cerrado.
Si el iframe alcanza el 0 primero, cancela el del padre para no duplicar.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Usa Web Notifications API via postMessage iframe→parent para alertar
incluso cuando el tab está en segundo plano o la pantalla ocupada.
Pide permiso al iniciar el protocolo (primer gesto del usuario).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Si _llamar no actualiza turnoActivo a en_servicio (fallo silencioso del
cliente), recuperarTurnoActivo() vuelve a consultar el servidor y carga
el turno correctamente, mostrando el botón rellamar.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
El banner de alerta solo se cerraba vía 'Ver paciente'. Si el profesional
hacía clic en 'Siguiente toma' directamente, el banner seguía parpadeando
y sonando aunque el formulario ya estuviera abierto.
- abrirTomaProgresiva() ahora llama cerrarBannerToma() + _tomaAlertados.delete()
- renderConsentimientos() preserva alertado:true si la siguiente_toma_at no
cambió, evitando beep doble al re-sincronizar después de firmar
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Agrega script inline en <head> de Layout.php, lugar.php y recepcion.php
que detecta prefers-color-scheme y activa data-bs-theme="dark" en <html>
antes del primer paint (evita flash de contenido blanco)
- sidebar.php: script de fallback para páginas con head propio no actualizado
- Layout.php: corrige background hardcodeado #f8f9fa en .main-content y
reemplaza bg-white por bg-body-tertiary en barra de título
- styles.css: elimina bloque @media duplicado y contradictorio; nuevo bloque
unificado cubre @media (fallback sin JS) + [data-bs-theme="dark"] para
elementos custom (sidebar, chat); Bootstrap maneja el resto automáticamente
- Bump styles.css a v=15 en los tres templates
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- guardar_toma.php: reconoce firma campo_id_svg (compat. registros históricos)
- get_consentimientos.php: mismo fix para conteo tomas_firmadas
- ver_formulario_enviado.php: $_mpMap incluye hora fija (Cortisol 3pm/4pm),
agrupa por condición de separador para no mezclar Glicemia/Cortisol,
muestra resumen de tomas al completar antes de cerrar el modal
- lugar.php: banner rojo + beep cuando el countdown de una toma llega a cero,
resetea alerta al cerrar el modal; función _playBeepLugar() compartida
DB Form 15: +10 campos (firma_profesional Minuto 60 Glicemia + campo
Resultado numérico en cada toma Glicemia y Cortisol)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- guardar_toma.php: filtrar firmas por condición del examen seleccionado; corrige
countdown espurio y detección de 'completado' cuando hay múltiples tipos de examen.
También descarta siguiente_toma_at si ya es pasado (minuto 0).
- ver_formulario_enviado.php: auto-scroll a primera firma pendiente al cargar el modal;
si el examen se selecciona y la firma no está en DOM, guarda y recarga el iframe.
Al completar tomas, cierra el modal directamente sin botón "Finalizar".
- get_cola.php: incluye siguiente_toma_at y en_toma_progresiva por turno desde
turnero_consentimientos para mostrar cuenta regresiva en tarjetas.
- lugar.php: muestra chip de cuenta regresiva en tarjetas de cola para pacientes
en toma progresiva; color cambia a warn (<2 min) y crit (¡Ahora!).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
ver_formulario_enviado.php:
- $_mpPrimeraPendiente, $_mpBloqueado y $fSvg buscaban la clave campoId_svg
pero guardar_toma.php guarda con clave campoId (sin sufijo). Se añade
fallback a ambas claves para compatibilidad hacia adelante y atrás.
lugar.php:
- new Date('2026-07-09 15:30:00') no parsea en Safari; se reemplaza el
espacio por 'T' para formato ISO antes de construir el timestamp.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- _guardarFirmaMP ahora llama guardar_toma.php en vez del draft POST,
guardando la firma con clave campoId (sin _svg) que es la que lee
get_consentimientos para el contador X/13.
guardar_toma.php también actualiza estado→en_progreso y siguiente_toma_at,
lo que activa el countdown en lugar.php via polling SSE.
- Al completar la última toma envía postMessage turneroFirmado para cerrar
el modal automáticamente.
- Botón cerrar del modal siempre habilitado (el operador puede cerrar
en cualquier momento durante el proceso).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- get_consentimientos: skip auto-create of consentimientos when solo_muestras=1
- get_consentimientos: add paciente_id to turno query; fallback muestras fetch
when no solicitud exists but turno has paciente_id (F-turns without reception)
- lugar: badge "Solo entrega de muestras" in ficha header for prioridad F
- lugar: hide sec-consent and sec-form-embebido when solo_muestras
- lugar: skip consent polling and form embedding for F-turns
- lugar: restore sec-consent visibility on resetFicha
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Token UUID generado en localStorage del navegador, persistido como cookie
- turnero_dispositivos.token: nueva columna para identificar equipos
- recepcion.php y lugar.php: chequean token (cookie) primero, IP como fallback
- configuracion.php: banner con token del equipo actual + botón "Registrar este equipo"
- save_dispositivo.php: acepta campo token al crear/editar dispositivo
- Migración: 20260709_dispositivos_token.sql
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
En lugar de confiar solo en la sesión (seteada al login), recepcion.php
y lugar.php ahora consultan turnero_dispositivos por IP en cada request.
Esto cubre sesiones preexistentes y evita que aparezca el selector cuando
la IP ya tiene un lugar asignado.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- ver_formulario_enviado: auto-fill médico (nombres/especialidad/código) en datos_prefilled desde solicitud
- ver_formulario_enviado: canvases de toma progresiva bloqueados secuencialmente — solo el turno activo es firmable, los siguientes aparecen al expirar el countdown
- ver_formulario_enviado: countdown prominente con reloj grande, color dinámico, sonido (Web Audio) y vibración al llegar la hora
- lugar: bloquear cierre del modal durante toma progresiva activa (botón X deshabilitado hasta mp_completar)
- lugar: bloquear Finalizar atención si hay muestras pendientes sin decisión (recibida/rechazada)
- lugar: _syncBtnFinalizar combina consentimientos + muestras para el aviso y estado del botón
- lugar: restricción de usuario a lugar específico via turnero_lugar_id (admin_users) + bloqueo JS en confirmarLugar()
- migrations: turnero_lugar_id en admin_users para restringir bacteriólogos por usuario
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Cuando la solicitud tiene solo_muestras=1, get_consentimientos.php
busca turnero_muestras pendientes de otras solicitudes del mismo
paciente y las antepone en el array muestras con es_pendiente_anterior=1.
lugar.php renderiza un tag 'visita anterior' en cada muestra previa
para que el bacteriólogo identifique el origen.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
El campo observaciones de la solicitud ya venía en el JSON de
get_consentimientos.php pero nunca se renderizaba en lugar.php.
Se agrega un bloque amber visible solo cuando sol.observaciones
tiene contenido, y se limpia al hacer resetFicha.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Al escribir en el textarea de comentarios se guarda automáticamente
en localStorage con clave toma_draft_<turno_id>. Al abrir la ficha
del mismo turno el texto se restaura con borde amarillo de aviso.
Al guardar exitosamente o al hacer resetFicha se borra el draft.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- DB: ALTER TABLE turnero_consentimientos ADD siguiente_toma_at, toma_inicio_at;
MODIFY estado para incluir 'en_progreso'
- guardar_toma.php (nuevo): guarda firma parcial por campo_id, parsea el
esquema para calcular siguiente_toma_at desde 'Minuto X' o hora fija,
marca 'en_progreso' o 'firmado' según tomas restantes
- get_consentimientos.php: expone siguiente_toma_at, es_toma_progresiva,
tomas_total, tomas_firmadas, campos_firma_pro en cada consentimiento
- lugar.php: estado en_progreso con badge X/Y, countdown en tiempo real,
animación de alerta cuando el tiempo llega, botón 'Siguiente toma' que
abre el modal embebido en modo toma_progresiva
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Agrega origen_lugar_id a turnero_consentimientos para distinguir consents
creados por examen (null) vs por estación (= lugar_id destino).
Recepcion filtra los de origen_lugar_id != null; lugar.php muestra
solo los suyos + los de examen.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- login.php: detectar dispositivo por IP al autenticar y redirigir al lugar asignado
- dashboard.php / lugar.php / recepcion.php: bloquear acceso por dispositivo (IP→lugar)
- lugar.php: modal firma profesional pre-guardada; compact 2-col en iframe; botón WA
solo cuando el formulario requiere firma del paciente
- ver_formulario_enviado.php: modo compact tablet, auto-save borrador (merge), firma
profesional en 1 clic, Muestras Prolongadas (countdown + Finalizar), auto_profesional
pre-fill desde sesión, fecha→fecha_hoy en campos de toma/recepción
- get_consentimientos.php: añadir requiere_firma_paciente para controlar botón WA
- save_firma_profesional.php: API para guardar firma pre-configurada del profesional
- migrations: turnero_dispositivos, admin_users.firma_svg, clon VIH F-LAB-05 para turnero
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Migraciones LIS 01-08: schema completo del nuevo LIS (secciones, protocolos,
ítems de resultado, perfiles, tarifas, empresas, histórico transaccional)
- ETL Firebird→MySQL: script CLI con conversión WIN1252→UTF-8, batches de 500,
resolución de FKs y deduplicación de pacientes
- turnero_muestras: tracking pendiente/recibida/rechazada por tipo de tubo
- lugar.php: widget de recepción de muestras (solo tipo=muestras)
- update_muestra_estado.php: API para marcar estado de muestra
- create_solicitud.php: auto-crea muestras al guardar solicitud
- get_consentimientos.php: incluye muestras[] en el response
- 6 vistas SQL: v_muestras_hoy, v_recepcion_completa, v_examen_precio, etc.
- numero_orden en encabezado del formulario firmado (D-/F- color diferenciado)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
lugar.php:
- Bell button enlarged to 44×44px touch target
- Fecha nac. / Celular fields hidden from bacteriologist ficha
- Cancel button added to station-selector overlay; shows only when changing
- ESC key closes overlay when a station is already selected
- visibilitychange pauses polling when tab hidden, resumes on return
recepcion.php:
- Consent action buttons: padding 2px 8px → 5px 12px, font-size .72→.82rem
- Exam list: per-category select-all checkbox with indeterminate state support
- guardarSolicitud: auto-calls pasarALugar() when no consents required
- abrirModalPaciente: nome field auto-focused for both new and edit cases
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
get_consentimientos.php ahora incluye s.numero_orden en el SELECT.
En lugar.php el número de orden aparece como badge de solo lectura
en el header de la ficha, junto al código del turno, cuando la
solicitud tiene número de orden asignado.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Agrega botón "Ver" en la sección Paciente de la ficha del turno.
Abre modal con datos completos del paciente (nombre, doc, fecha nac,
celular, médico, embarazo) y timeline de historial de visitas anterior
(misma lógica que recepción), cargado desde get_historial.php.
El botón solo aparece cuando hay un paciente vinculado al turno.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Bloque 1 - Cola:
- Separate sections for "en servicio" vs "en espera" with headers
- Time-in-queue badges (green <10min, amber <25min, red 25min+) from creado_at
- Prominent EN SERVICIO pill (solid green) instead of subtle border
- Larger touch targets on queue cards, bigger bell button
- Count chips show espera + servicio counts separately
Bloque 2 - Ficha header + action bar:
- Patient name is now the dominant element (1.35rem bold) with skeleton
loader animation while async data loads; turno code becomes a secondary chip
- Action bar restructured: full-width primary button (blue "Anunciar turno" /
green "Finalizar atención") + smaller secondary row (Re-llamar, Regresar, Ausente)
- Consent warning strip added inside action bar showing pending count;
blocks Finalizar visually and functionally
Bloque 3 - Sections + mobile:
- Section headers use consistent .ficha-sec-hdr style with icon
- Mobile: fixed sticky "← Cola" top bar inside ficha panel (always visible,
no hunting for back button)
- ficha-turno uses flex-column so action bar sticks to bottom without scroll
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Eliminar sec-form-embebido como sección independiente
- En modo embebido: fila de consentimiento muestra botón "Firmar" que abre modal directamente por token
- En modo link: botones "Firmar presencial" + "WA" como antes
- abrirModalConsentimientoPorToken(token) recibe token directo sin cache
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- ver_formulario_enviado.php: usar opciones??options??items como fallback de claves del schema
- ver_formulario_enviado.php: marcar _esquemaTieneFirmaPaciente en cualquier campo firma (no solo sin SVG), evita mostrar firma paciente cuando schema solo tiene firma_profesional
- ver_formulario_enviado.php: modo embebido (?embed=1) con layout compacto para iframe
- lugar.php: pasar &embed=1 al iframe src
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>