Commit Graph
100 Commits
Author SHA1 Message Date
Lizandro GuarnizoandClaude Sonnet 4.6 fb448d2c2f fix(bandeja): labels de muestras de vuelta a 'pendientes' en UI
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-30 18:04:50 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 22ed64d8ad fix(bandeja): filtrar muestras rechazadas en vez de pendientes
- 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>
2026-07-30 18:04:23 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 0e0dc4bd44 refactor(encuesta): usar paciente_cel directo igual que send_consentimiento
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>
2026-07-30 17:57:36 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 9a18184ad7 fix(encuesta): usar paciente_cel directamente si no está en tabla users
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>
2026-07-30 17:55:57 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 a4ddc63b6f fix(encuesta): ocultar barra si paciente no tiene celular registrado
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>
2026-07-30 17:55:10 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 90e3125e45 feat(historial): mostrar nombre kiosko y nombre BD en columna paciente
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>
2026-07-30 17:52:25 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 7e44fd4050 fix(historial): corregir SyntaxError por backtick/backslash en renderTabla
- esc() ahora escapa backticks (&#96;) 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 &#39;

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-30 16:43:44 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 25bdf9aed2 fix: resetear toma limpia siguiente_toma_at y toma_inicio_at
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>
2026-07-30 16:04:51 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 90d4e1bc49 fix: nombre profesional bajo firma firmada; ver paciente desde espera toma
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-29 21:32:03 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 309c1c93c6 fix: nombre y cédula del profesional en cada toma de muestras prolongada
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-29 21:24:32 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 2455417bb4 feat: encuesta de satisfacción post-finalizar toma de muestras
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-29 21:23:07 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 4a4cbc31b8 fix: permitir guardar borrador en estado enviado desde turnero
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-29 21:16:11 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 c85181ad56 feat: mostrar nombre y cédula del profesional bajo la firma
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-29 21:10:33 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 806ae0b552 fix: colapsar márgenes !important de secciones condicionales ocultas
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-29 21:01:51 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 8a384088c3 fix: radios con 3 opciones span full en modal compact
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-29 20:55:13 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 4fee7f753a fix(turnero): anexar_formulario usa UUID() en vez de bin2hex para el token
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>
2026-07-29 20:50:57 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 fcfdcccba9 fix(consentimiento): resetear firma conserva datos_respuestas para re-firma
El usuario puede modificar y volver a firmar sin perder lo llenado.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-29 20:49:23 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 bf8478244b fix(formulario): reducir espacios en blanco en modo compact del modal
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-29 20:44:36 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 8937a46362 fix(formulario): radio options en fila horizontal — override display:flex + fix form_cliente
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-29 20:42:29 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 7bb69f6efd fix(formulario): opciones de radio en fila horizontal en vez de vertical
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-29 20:40:16 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 ad5a4c834f fix(lugar): alarma de toma progresiva suena en panel de espera entre muestras
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>
2026-07-29 20:34:22 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 2da5960596 fix(bandeja): default hoy + etiqueta Hoy/Ayer/fecha en cada registro
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-29 20:32:08 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 00f6e7f759 fix(historial): default hoy en vez de 7 días para evitar timeout inicial
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-29 20:30:37 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 e3216faaa5 feat(bandeja): chips clickeables para muestras pend. y prog. en espera
- 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>
2026-07-29 20:29:45 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 118e98b360 feat(bandeja): filtros por fecha, nombre, # orden y estado
- 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>
2026-07-29 20:22:22 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 4cb133010a feat(bandeja): filtro de muestras pendientes
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>
2026-07-29 20:21:06 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 2e3534ab02 feat(bandeja): filtro por estado en la bandeja del día
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-28 12:02:37 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 7d444699d3 feat(turnero): panel de estado de muestras en recepción
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>
2026-07-28 11:59:18 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 7b599b28ef fix(turnero): bacterióloga sin token va al dashboard, no a lugar_id=1
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-28 11:55:30 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 8472c916b0 fix(turnero): llamar_desde_espera actualiza lugar y dispara display
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>
2026-07-28 11:49:37 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 3d26700e16 feat(recepcion): mostrar contador de exámenes seleccionados
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>
2026-07-28 09:41:06 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 fa591873a9 fix(auth): redirigir admins/supervisores al dashboard del turnero tras login
login.php y index.php enviaban a v=recepcion a cualquier usuario con el
módulo turnero que no fuera bacteriólogo, incluyendo superadmin, admin
y supervisor. Esto causaba que al cerrar sesión y volver a entrar, el
admin aterrizaba en recepción en vez del dashboard.

Ahora:
- bacteriologo → lugar_id=1
- superadmin/admin/supervisor → v=dashboard
- recepcionista/otros → v=recepcion (comportamiento previo correcto)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-28 09:30:38 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 925b29b00b fix(turnero): incluir solo_muestras en llamar_turno y bypass consent para entrega
- 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>
2026-07-28 09:20:56 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 dffb242522 perf(historial): reemplazar N+1 queries por 3 batch queries
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>
2026-07-28 08:54:04 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 70fda3dacc fix(turnero): dos correcciones de estado y redirección
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>
2026-07-28 08:40:49 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 88fd0e4151 fix(turnero): login detecta dispositivo por token antes de buscar por IP
La detección de tablet fija solo buscaba por IP; tablets con token
configurado no hacían match y caían al redirect de rol (recepcion).
Ahora busca primero por cookie turnero_token y como fallback por IP.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-28 08:16:37 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 e41663cff0 fix(turnero): bacteriologo inicia en lugar_id=1 en lugar de dashboard
El rol bacteriologo iba a v=dashboard al hacer login. dashboard.php solo
redirige automáticamente si la IP está registrada como dispositivo; sin
IP registrada, la bacterióloga se quedaba en el dashboard sin usar.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-28 08:14:54 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 afa7cc6812 feat(turnero): anexar formulario adicional a turno desde lugar.php
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>
2026-07-28 07:49:06 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 ff52fa5ba7 feat: olvidar firma, editar tiempos tomas, obs en header, fix dispositivos
- 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>
2026-07-28 07:37:09 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 8e9d7750a7 fix: revertir selección de voz a es-CO (online) — mejor calidad
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-28 06:49:35 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 cbcc0a9d8d fix: voz TTS no sonaba en display_global por voz online y falta de activación
- 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>
2026-07-28 06:47:19 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 4b054c5afc fix: meta ignorado en sendTemplateMessage con rawComponents
Cuando se usaban rawComponents, el parámetro $meta no se agregaba
al payload, causando que los mensajes del turnero se guardaran con
canal='bot' y aparecieran en conversations.php.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-28 01:14:53 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 a08e2002c4 chore: modelo Gemini 2.5-flash → 3.5-flash
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-28 01:11:14 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 f5ef28e2c7 chore: actualizar modelo Gemini 2.0-flash → 2.5-flash
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-28 01:09:10 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 4142c67a5d fix: modal cerrar anticipado no abría por d-none !important
Bootstrap d-none usa display:none !important que gana sobre
modal.style.display = 'flex' del JS. Se quita la clase d-none;
el display:none del style inline es suficiente.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-28 01:02:47 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 ab8b0702a7 fix: handle nested _tomas_config format in guardar_toma.php
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>
2026-07-28 00:48:34 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 70077d7fd4 fix: campos condicionales ocultos no dejan espacio en blanco
- JS evaluar(): añade margin:0; padding:0 al ocultar elementos con maxHeight:0
  para que .campo-edit no deje margen residual
- PHP render: baked-in separator oculto incluye margin:0;padding:0

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-28 00:32:23 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 c9ea76f782 feat: cierre anticipado de tomas prolongadas con justificación
- 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>
2026-07-28 00:29:32 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 915fa3ba5c feat: config anidada por examen en tomas prolongadas + F-LAB-08 mejoras
- 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>
2026-07-28 00:21:24 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 0ea411ce62 Fix: preservar todos los exámenes seleccionados al configurar tiempos
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>
2026-07-27 23:48:45 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 b13cc3f8ea Fix: config tiempos por examen con grupos compartidos
- every en lugar de some: examen configurado solo cuando TODOS sus grupos
  tienen config (no solo alguno compartido)
- inSaved sin requisito isModify: grupos compartidos muestran selección
  previa al abrir panel de otro examen, evitando sobreescribir config

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-27 23:42:49 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 9ebd22e41c Fix: auto-open panel tiempos solo cuando hay examen sin configurar
El fallback que abría el primer examen en modo editar cuando todos estaban
configurados causaba un loop infinito — el panel nunca se cerraba y el
usuario no podía llegar a firmar. Ahora solo se auto-abre si hay un examen
pendiente de configuración.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-27 23:36:19 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 abbd5bde16 Fix: get_especialidades muestra pacientes en_espera_lugar
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>
2026-07-27 23:30:49 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 a3c19bf4e7 Turnero: toma progresiva por examen, Rubeola solo-profesional, kiosko
- 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>
2026-07-27 23:22:42 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 4012ae97be turnero: sala de espera entre muestras (toma progresiva)
- 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>
2026-07-27 22:41:34 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 e634eb8932 turnero: panel especialidades (ginecología/pediatría) en toma de muestras
- 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>
2026-07-27 22:31:11 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 b1d1c0896f turnero: kiosko — niños placeholder+edad, muestras desc, numpad +5%
- Prioridad A (Niños): placeholder cedula → 'Tarjeta de Identidad del menor (si tiene)'
- Prioridad A: age check via check_paciente.php → toast aviso si paciente ≥ 10 años
- Prioridad F: descripción → 'Muestras de días anteriores' (BD + fallback PHP)
- Numpad: padding .6rem → .63rem .6rem (+5% alto)
- Nuevo: check_paciente.php (endpoint público, solo devuelve fecha_nacimiento)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-27 22:18:38 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 80b61f88be turnero: renombrar estado 'rechazada' → 'Pendiente' en UI
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>
2026-07-27 22:08:41 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 045fd64113 feat(bandeja): modal de consentimientos + badges visibles confirmados
- 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>
2026-07-27 21:48:09 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 b76c2f11e9 feat(bandeja): badges de comentarios y estado en tarjeta de lista
- 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>
2026-07-27 21:42:55 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 0b39e3f0d0 fix(consecutivo): contador atómico con LAST_INSERT_ID — elimina race condition
Reemplaza SELECT MAX() (vulnerable a concurrencia) por UPDATE atómico sobre
turnero_consecutivos. Dos recepcionistas simultáneas nunca pueden recibir
el mismo número de orden. Sin UNIQUE constraint, sin riesgo en datos históricos.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-27 21:41:10 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 9f251d57be fix(bandeja): URL consentimiento → ver_formulario_enviado.php?token=...
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-27 21:31:21 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 368c4c8a5a feat(bandeja): vistos/por-revisar, filtro nombre+examen, marcar como visto
- Reemplaza activos/cerrados por pendientes (por revisar) / vistos
- Columna bandeja_visto_at en turnero_turnos; API marcar_visto.php
- Botón "Marcar como visto" → mueve paciente a sección inferior
- Filtro en tiempo real por nombre y por código de examen (GROUP_CONCAT)
- URL de consentimientos: relativa (consent.php?token=...)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-27 21:29:52 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 6c54674795 fix(bandeja): corregir res.data → top-level keys (jsonOk no anida en data)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-27 21:21:01 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 0c6e522106 feat(turnero): Bandeja del día — lista de pacientes con detalle y cierre
Vista split: lista ordenada por numero_orden (activos arriba, cerrados abajo),
stats bar con totales, panel de detalle con exámenes por categoría, muestras,
consentimientos, comentarios, PDF via window.print() y acción de cerrar turno.
Auto-refresh cada 30s. Visible para bacteriólogos y admins.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-27 21:17:48 -05:00
Lizandro Guarnizo 3c3e531e48 feat(lugar): botón quitar consentimiento con motivo obligatorio y auditoría 2026-07-27 20:56:44 -05:00
Lizandro Guarnizo deb74a1f9e fix(login): bacteriologo redirige a v=dashboard para que App.php lo intercepte correctamente 2026-07-27 07:40:12 -05:00
Lizandro Guarnizo 75c7cb2762 feat(audit): registrar en lab_actividad_admin quien edita formularios de lugar 2026-07-27 07:27:56 -05:00
Lizandro Guarnizo 8dadf0ba7a fix(login): bacteriologo redirige a lugar en vez de recepcion al iniciar sesion 2026-07-27 07:08:15 -05:00
Lizandro Guarnizo 6192ef89a1 fix(kiosko): height calc(100vh/0.80) compensa zoom para llenar viewport 2026-07-27 06:54:52 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 1660a5a426 fix(kiosko): zoom en html en vez de .kiosko-wrap para llenar viewport
zoom en el elemento hijo encogía 100vh dejando espacio vacío abajo.
En html el browser recalcula vh correctamente y el layout llena la pantalla.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-27 06:50:28 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 ea956f2c6c fix(display_global): convertir nombre a Title Case antes del TTS
Los nombres en MAYÚSCULAS son deletreados por el motor de voz.
Convertir a Title Case para que se lean como palabras normales.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-27 06:44:36 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 40b8b54aee fix(display_global): no cortar TTS cuando llaman desde lugar simultáneo
Elimina speechSynthesis.cancel() para que los anuncios no se interrumpan.
El overlay espera a que el TTS termine (onend) antes de pasar al siguiente,
con un mínimo visual de 5 segundos.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-27 06:41:26 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 b4e45c1c22 feat(lugar): formularios en modal 10% más grandes en tablet
Pasa zoom=1.1 al iframe; ver_formulario_enviado.php aplica zoom al body.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-24 10:58:46 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 e93bb1ad41 fix: evitar que <?xml rompa PHP con short_open_tag
El parser de PHP interpreta <? en HTML mode cuando short_open_tag=On.
Se rompe la cadena para que no aparezca el literal <? en el archivo.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-24 10:14:04 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 5400fd9b32 fix: exportar Excel como SpreadsheetML (.xls) en vez de CSV
Abre directamente en Excel con columnas formateadas, sin asistente
de importación ni apertura en VS Code.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-24 10:02:12 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 dc5fd6e272 feat(chat): separadores de fecha estilo WhatsApp
Muestra "Hoy", "Ayer" o la fecha entre grupos de mensajes de días distintos.
El timestamp del bubble ahora solo muestra la hora.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-24 09:58:08 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 42662c2ade fix(kiosko): reducir interfaz al 80% con zoom
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-23 13:22:07 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 4cd307ba4f feat(lugar): alarma persistente para tomas progresivas
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>
2026-07-23 12:45:22 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 2ed9eff653 form_cliente: leer inputs de texto en evaluador de condiciones
Permite usar campos linked (texto) como controladores de secciones
condicionales; necesario para ocultar datos obstétricos según género.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-23 12:33:16 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 4f58d6bf16 fix(kiosko): quitar ceros iniciales del barcode de cédula
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-23 11:57:19 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 acac557f90 fix(kiosko): botón Volver visible arriba en pantalla de cédula
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-23 11:47:26 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 1291d0368a fix(kiosko): reducir numpad otro 15%
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-23 11:25:02 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 960867d785 fix(kiosko): bloquear campo al primer Tab del escáner + numpad más pequeño
El Tab es la señal de fin de cédula en el barcode. Al recibirlo se
activa _scannerLocked y se ignoran todos los caracteres siguientes
(apellidos, fecha, sangre) hasta que llegue Enter o se limpie el campo.
También se redujo el numpad ~10% para que entre en pantalla.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-23 11:22:18 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 5c02a8ea99 fix(kiosko): numpad debajo del WhatsApp, escribe al campo activo
Restaura campo WhatsApp (inputmode=none), mueve el numpad debajo.
El numpad detecta cuál input tiene foco y escribe en ese campo.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-23 11:19:23 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 4e945e6437 fix(kiosko): eliminar campo WhatsApp del flujo, botón OK en numpad
El campo WhatsApp generaba fricción innecesaria. Ahora el numpad
tiene un botón "Obtener mi turno" de ancho completo como última fila.
WhatsApp queda como hidden vacío para no romper la API.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-23 11:18:25 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 104ba07af7 feat(kiosko): numpad táctil para campo cédula
- inputmode=none en inp-cedula suprime el teclado Windows
- Grid 3×4 con dígitos 0-9, C (borrar todo) y ⌫ (borrar uno)
- pointerdown + preventDefault mantiene el foco en el input
- El escáner sigue funcionando (escribe por keydown, no por IME)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-23 11:12:31 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 43f14c2571 fix(kiosko): salida admin cierra Chrome con window.close()
Navegar al ERP dentro del kiosk no sirve porque --kiosk es un flag
del proceso. window.close() cierra Chrome y devuelve el escritorio
Windows táctil, desde donde pueden abrir el ERP normalmente.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-23 11:03:21 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 e84c16f875 feat(kiosko): salida admin con 7 toques en el logo
Toca el logo o el nombre del laboratorio 7 veces en 4 s para abrir
el overlay de salida: ir al ERP, recargar kiosko o cancelar.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-23 10:59:30 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 aaeb3567c2 fix(kiosko): teclado táctil Windows reaparece al tocar el campo
- blur() en mostrarTicket, seleccionarOtraPrioridad y volverPrioridades
  para que ningún campo quede enfocado al cambiar de pantalla
- pointerup en cada input: si ya estaba enfocado, blur+focus(30ms)
  fuerza a Windows a reinvocar el teclado táctil
- El focus programático de seleccionarPrioridad se conserva (escáner)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-23 10:57:22 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 4d75f82b30 fix(kiosko): cortar en primer no-dígito, no solo en espacio/tab
El Tab se bloquea pero las letras seguían escribiéndose en el campo.
Ahora cualquier carácter no numérico trunca el valor inmediatamente.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-23 10:54:04 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 f8903a4982 fix(kiosko): bloquear Tab del escáner para que no salte al campo WhatsApp
El lector de cédula envía los campos con Tab entre ellos. Se previene
el cambio de foco en keydown. Si algo llega igualmente a inp-cel
(letras) se limpia y regresa el foco a la cédula.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-23 10:50:00 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 24211de0e9 fix(kiosko): truncar código de barras al primer campo (solo cédula)
El escáner envía cedula+nombre+apellidos+… en una sola ráfaga.
Se corta en el primer espacio/tab para quedarse solo con los dígitos.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-23 10:37:36 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 5f9e7032a0 fix(chat): mostrar fecha en timestamp de mensajes de días anteriores
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-22 22:36:02 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 a15dceae2a feat(turnero): devolver paciente a recepción desde toma de muestras
- 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>
2026-07-22 22:22:05 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 728d78f606 fix(kiosko): corregir touch en Edge kiosk mode
- touch-action: manipulation directo en cada botón e input (no solo en body)
- -ms-touch-action para compatibilidad Edge legacy
- position: relative en .kiosko-wrap para contener screens absolutas
- inset: 0 en .screen.hidden para que no se salga del contenedor

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-22 22:12:19 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 868263fe6f fix: no usar firma_svg global como fallback cuando hay datos por-campo
Formularios con 2 campos firma (consentimiento + disentimiento)
mostraban la firma del campo 2 también en el campo 1 porque
firma_svg se usaba como fallback del primer campo. Ahora si
datos_respuestas ya tiene algún _svg, se confía solo en los datos
por-campo y se ignora el fallback global.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-22 21:57:34 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 136b123cc0 fix: preview en tiempo real de firma Topaz en modal
Usar GetSigImageB64 en el poll para actualizar el canvas del overlay
cada vez que cambia el número de puntos. SigImage/0 (SigWebRefresh)
solo devuelve el trazo actual; SigImage/1 acumula toda la firma.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-22 21:45:25 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 719f27d863 fix: captura de trazos Topaz — canvas context + funciones correctas
- Agregar <canvas id="topaz-canvas"> al overlay de ambos archivos
- SetTabletState(1, canvasCtx, 50) con contexto 2D (sin esto los trazos no se dibujan)
- Guardar timer retornado y pasarlo a SetTabletState(0, tmr) al cerrar
- NumberOfTabletPoints() en vez de GetSigTotalPoints() (función inexistente en v1.0.4)
- GetSigImageB64(callback) async en vez de llamada síncrona sin valor de retorno

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-22 21:38:56 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 78b01033c5 fix: cargar SigWebTablet.js desde /assets/js/ en form_cliente
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-22 21:26:27 -05:00