API: agrega por_prioridad con último código llamado a muestras por letra.
Vista: badges con color de prioridad mostrando "A: A-023", "B: B-011", etc.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- API: agrega tipo_documento y numero_documento al SELECT
- Vista: línea 1 = nombre BD + CC/doc, línea 2 gris = Kiosko: nombre
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- APIs: total_min = creado_at → fin_lugar_at (solo finalizados)
- Dashboard: KPI card 'Total prom. puerta a puerta' + columna Total en tabla
- Historial: columna Total en tabla
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Usaba ['id'] sobre un int → sesionId=0 → sin resultados.
Cambiado a sesionHoy() que sí retorna array con 'id'.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- get_bandeja.php: contar estado='rechazada' (no 'pendiente')
- chip renombrado a "Muestras rechaz." con ícono fa-flask
- card muestra badge rojo con ícono y cantidad cuando hay rechazadas
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Elimina el lookup innecesario en users/$userId que nunca se usaba.
Ahora sigue el mismo patrón que recepción: BD telefono → paciente_cel.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
El paciente siempre tiene celular registrado; el error ocurría porque se
requería match en users. Ahora usa paciente_cel como fallback final.
Revierte la guarda del frontend del commit anterior.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Si turnoActivo.paciente_cel está vacío, _ofrecerEncuesta retorna sin
mostrar la barra, evitando el error de WA al intentar enviar.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Si el nombre registrado en BD difiere del ingresado en kiosko, muestra
ambos: el BD como principal y el kiosko en gris debajo.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- esc() ahora escapa backticks (`) para evitar que nombres de paciente
con backticks rompan el template literal
- onclick simplificado: eliminado .replace(/'/g,\\'\\\\\\'\\'') que era JS
inválido (backslash fuera de string literal dentro de ${}); esc() ya
convierte comillas simples a '
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Al olvidar una toma los tiempos del ciclo anterior quedaban en la DB,
haciendo que al re-firmar la toma reseteada las demás aparecieran
inmediatamente (sin countdown) y el profesional tuviera que firmar 3
veces en vez de 1.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
bin2hex(random_bytes(16)) genera hex sin guiones que no pasa la validación
de ver_formulario_enviado.php — el formulario adicional daba token inválido
al intentar firmar.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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>
- Chip "Muestras pend." y nuevo "Prog. en espera" son toggles de filtro
- Se resaltan con borde al activarse; combinables entre sí y con el dropdown
- API retorna muestras_pendientes (turnos con ≥1 muestra pendiente) y
prolongadas_pendientes (toma progresiva en espera entre extracciones)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Inputs desde/hasta pasan al API y filtran por rango de fechas
- Buscador cubre nombre, número de orden, código y examen
- API usa fecha cuando se pasa, sesión actual como fallback
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Añade conteo de muestras_pendientes por turno en la lista y opción
de filtro en el selector de estados de la bandeja del día.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Recepción ve en una barra inferior qué turno atiende cada estación de
toma de muestras (verde = ocupada, gris = libre), cuántos están en
espera y cuántos en espera entre muestras progresivas. Se refresca cada 7 s.
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>
Badge junto al título "Exámenes solicitados" que muestra cuántos
exámenes lleva el turno actual. Se actualiza en tiempo real al
agregar o quitar ítems del TomSelect.
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>
Las 3 subqueries por turno (examenes, consentimientos, comentarios)
× 50 turnos = 150 round-trips al servidor remoto (~83ms c/u) sumaban
12.5s de espera, causando timeouts o página sin carga.
Ahora se agrupan en 3 queries con IN(?) sobre todos los IDs del lote.
Tiempo total: ~357ms (35x más rápido).
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>
- getVozES() prefiere voces locales (offline) sobre Microsoft Online que
fallan silenciosamente sin internet o con latencia
- cancel() antes de speak() para limpiar cola atascada
- keepalive sin check !speaking, cada 10s
- sonidoActivo persiste en localStorage; banner rojo si no activado
- AudioContext se resume si suspended antes de reproducir
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
PHP 8.1+ throws TypeError when using an array as array key.
Old flat-format loop iterated nested { examName: { group: [fids] } }
and tried $allowed[$arrayValue] = true, causing a fatal error and
returning ok:false ("Error al guardar") on every toma signing attempt.
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: almacena _tomas_config como { examName: { groupKey: [fids] } }
en vez de flat, detecta y resetea formato antiguo, valida exam_type requerido
- ver_formulario_enviado.php: rendering aplana nested→flat para filtrar secciones;
JS lee _mpTomasConfig[examName][gk] en inSaved y _mpRenderBadges; auto-open
verifica per-examen; _mpActiveConfigExam evita race entre exámenes simultáneos
- get_consentimientos.php: flatten de nested o flat para calcular allowedFids
en ambos loops de tomas progresivas
- F-LAB-08 (id=16): añade 'Grupo sanguíneo' a opciones de _m5n865g;
agrega campos condicionales '¿Qué tipo de cáncer?' y 'Especifique grupo sanguíneo'
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Al guardar config de un examen se sobreescribía _c8j2g16 con solo ese examen,
perdiendo los demás en el reload. Ahora se envía exam_types (array completo),
se guarda como array en datos_respuestas y sessionStorage restaura todos los
checkboxes marcados al recargar.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Antes solo aparecían pacientes con inicio_lugar_at IS NOT NULL (ya llamados).
Ahora incluye estado IN ('en_espera_lugar','en_servicio') para mostrar
pacientes enviados a ginecología/pediatría antes de ser llamados.
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>
- Consentimientos abren en modal iframe (ver_formulario_enviado.php?embed=1)
- Igual que en toma de muestras: spinner de carga, click fuera para cerrar
- Badges de comentarios (Recep./Toma/Gral.) y estado (Finalizado/En servicio)
confirmados visibles en tarjeta
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Indicadores por tipo: Recep. (azul) / Toma (verde) / Gral. (amarillo)
- Label de estado del servicio: En servicio / En espera / Finalizado / etc.
- Query agrega SUM por tipo de comentario sin subquery adicional
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>