Commit Graph
361 Commits
Author SHA1 Message Date
Lizandro GuarnizoandClaude Sonnet 4.6 12c790fb5c fix: mover CSS del modal nuevo-mensaje fuera del media query mobile
Los estilos de #nuevo-modal y .nv-* estaban dentro de @media (max-width: 680px),
por lo que el modal no tenía diseño en desktop.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-03 17:28:08 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 f975844d9d fix: reacciones y audio en chat turnero
- chat_react: guardaba en tabla `messages` en lugar de `conversations` (tabla real del chat)
- chat_react: columna era `whatsapp_message_id`; la correcta es `message_id`
- chat.php: sendReactionEmoji actualiza la burbuja DOM inmediatamente sin esperar poll
- chat_upload_media: mime_content_type() devuelve video/webm en Linux para audio grabado;
  ahora cualquier mime con "webm" se remapea a audio/ogg antes de determinar mediaType

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-03 17:24:35 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 ab72750410 feat(chat): botón Nuevo mensaje con búsqueda de paciente y picker de plantilla
- chat_buscar_paciente.php: GET ?q= busca en lab_pacientes por nombre,
  documento o teléfono (mín. 2 chars, máx. 20 resultados)
- chat_start_conversation.php: POST {phone, template_name, lang, params}
  — normaliza teléfono, busca/crea usuario en users, envía plantilla
  vía WhatsAppService('turnero'), devuelve {ok, user_id}
- chat.php: botón "+ Nuevo" en sidebar header; modal 3 pasos:
  1) buscar paciente (debounce 350ms) o ingresar número libre
  2) lista de plantillas habilitadas
  3) variables de la plantilla + Enviar
  Al enviar recarga el sidebar y abre la conversación automáticamente

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-03 17:19:18 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 3d6147bf0d fix(template): send parameter_name for named-variable templates
WhatsApp API returns "(#100) Invalid parameter — Parameter name is
missing or empty" for templates that use named variables.

- chat.php sendTemplate(): collect params as [{name, value}] objects
  using data-var-index as the parameter name
- WhatsAppService::sendTemplateMessage(): handle {name, value} objects
  in the indexed-array path by emitting {type, parameter_name, text}
  (backward-compatible — plain strings still work as positional params)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-03 15:04:31 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 f60df038d8 fix(chat): capture template name/lang before closing modal in sendTemplate
closeTemplatePicker() sets _tplSelected = null, so accessing
_tplSelected.template_name after the call threw a TypeError caught
as "Error de red al enviar plantilla".

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-03 14:47:31 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 548ad3140d feat(turnero): template whitelist checklist + 2-step picker in chat
- configuracion.php (sesion tab): loads approved message_templates and
  enabled turnero_chat_plantillas; renders styled checklist; guardarPlantillasChat()
  POSTs to chat_save_plantillas.php
- chat.php: replaces free-text template modal with 2-step modal —
  step 1 lists enabled templates (chat_get_plantillas.php), step 2
  fetches variables via get_template_details.php and renders labeled
  inputs; sendTemplate() builds params array and POSTs to chat_send_message.php

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-03 14:37:21 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 00725ccd11 Redesign lugar.php UI/UX: patient-first, better cola, sticky actions
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>
2026-07-03 14:21:42 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 1e2cfbaa0b refactor(lugar): embebido como botón en fila de consentimiento, no sección separada
- 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>
2026-07-03 12:37:50 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 a0189a61eb feat(lugar): formulario embebido como modal en lugar de iframe inline
- Botón "Abrir formulario de firma" en ficha abre modal overlay
- Modal con iframe cargando ver_formulario_enviado.php?embed=1
- postMessage 'turneroFirmado' cierra modal, refresca consentimientos y oculta botón
- Token pre-cargado al abrir ficha para apertura rápida del modal

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-03 12:16:22 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 27135e4d2f fix(formulario): múltiples correcciones modo embebido/turnero
- 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>
2026-07-03 12:15:34 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 7b734195e0 fix(lugar): incluir formulario_modo en query de lugares
La columna no estaba en el SELECT, siempre quedaba 'link'.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-03 12:01:59 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 5dc2b0efa8 fix(turnero): corregir envío WA desde recepcion
- recepcion.php: quitar guard !solicitudActiva en enviarConsentimientosTodos (bloqueaba silenciosamente antes de guardar solicitud)
- send_consentimiento.php: eliminar requisito de solicitud y exámenes para poder reenviar; usar WhatsAppService('turnero') con rawComponents correctos (1 body param=codigo, button param=token); JOIN lab_pacientes también via t.paciente_id como fallback de teléfono

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-03 11:59:40 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 2dec300f3a Fix professional signature row only showing when form schema requires it
Only display the 'Falta firma enfermero' row and button when the assigned
consent form actually contains a firma_profesional field in its esquema.
get_consentimientos.php now computes requiere_firma_profesional from the
JSON schema and recepcion.php gates the row on that flag.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-03 11:55:20 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 f8245b4d6f feat: modo embebido de formulario por lugar (link vs iframe)
- Nueva columna turnero_lugares.formulario_modo (link|embebido)
- Config: radio en modal de edición para elegir el modo
- lugar.php: muestra iframe con ver_formulario_enviado al activar turno
- API get_consent_token.php: crea/obtiene token para turno+lugar

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-03 11:35:45 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 707ef24572 configuracion: sección perfil WhatsApp Business (foto, about, dirección, email, webs, categoría)
- wa_profile.php: GET carga perfil desde Graph API, POST guardar campos, POST foto sube imagen
- configuracion.php tab sesión: UI con foto de perfil editable, campos del negocio y botón guardar

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-03 10:08:41 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 9d57bbac5c recepcion: historial paciente muestra 5 visitas por página (antes 15)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-03 09:16:44 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 00aee615c5 lugar: cambiar label 'TOMA DE MUESTRAS' → 'ENTREGA DE MUESTRAS'
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-03 08:25:30 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 17703f0157 fix(chat-turnero): prevent messages mixing between contacts on fast switching
Capture activeUserId at the start of each async function (fetchMessages,
loadMoreMessages, pollMessages) and discard the response if the user changed
contacts while the request was in-flight.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-03 08:19:52 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 a0979e31e7 recepcion: quitar botón duplicar del historial (va en sección formatos)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-03 08:19:27 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 78e71d5926 recepcion: mostrar kiosko + paciente vinculado en dos líneas separadas
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-03 08:15:03 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 c511da18bd recepcion: fix solo_muestras, reorder fields, duplicar visita, kiosko label
- create_solicitud.php: evitar SQL `IN ()` vacío cuando solo_muestras=1 sin exámenes
- recepcion.php: mover embarazada+médico después del historial del paciente
- recepcion.php: actualizar label kiosko con nombre real al vincular paciente
- recepcion.php: botón "Duplicar" en historial para copiar exámenes y pago de última visita
- recepcion.php: omitir validación de consentimientos pendientes cuando es solo_muestras
- recepcion.php: agregar alias esc() = escHtml() usado en buscarMedico

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-03 08:13:38 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 d3826972ad feat(turnero): selector de médico tratante en recepción, visible en toma de muestras
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-03 00:55:32 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 a7ab91615f feat(chat-turnero): rich messaging UI — images, video, audio, reactions, emoji, templates
- Attach button with file preview (image thumb for images, name+size for others)
- Audio recording via MediaRecorder API with live timer; sends as voice note
- Emoji panel with 35 common emojis (medical/comms focused)
- Reaction picker on message hover — sends via chat_react.php API
- Template modal (name, lang, JSON params) — sends via chat_send_message.php type=template
- buildBubble() now renders: inline images, <video> player, <audio> player, doc links with type icon
- Shows reaction_emoji below bubble and status tick on outgoing messages

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-03 00:53:23 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 cb77425ca2 feat(turnero): vista verificar_paciente dentro del ERP con Layout
- Crea modules/turnero/views/verificar_paciente.php con Layout::open/close
- Flujo completo: cédula → OTP → éxito, mismo JS que verificar.php público
- Link del módulo ahora apunta a erp.php?m=turnero&v=verificar_paciente

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-03 00:35:00 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 fd95e7b38a fix: restaurar todas las optimizaciones móvil/tablet perdidas por force push
- chat.php: recuperado y re-aplicado panel-switch móvil, viewport iOS,
  safe-area, sticky topbar, link ← Menú, botón volver
- lugar.php: panel-switch tablet (≤860px), botón ← Cola, JS mobile
- recepcion.php: panel-switch tablet (≤900px), botón ← Cola, JS mobile
- sidebar.php: soporte atributo target en links de módulo

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-03 00:25:47 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 98507ad1a5 fix(turnero): restaurar dos plantillas WhatsApp en tab Sesión
- Plantilla con consentimiento (turnero_wa_template)
- Plantilla muestra pendiente (turnero_wa_template_muestra)
- sesion_turno.php guarda ambas en lab_config

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-03 00:17:17 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 34004b4d98 feat(turnero): opción solo entrega de muestras con prioridad y badge naranja en cola
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-03 00:07:05 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 76dda4722a feat(turnero): pago combinado con desglose por efectivo, transferencia y tarjeta
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-03 00:02:48 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 4626ca3f28 feat(display): nombre paciente más grande y visible en overlay de llamada
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-02 23:58:09 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 38bd211ca6 feat(kiosko): mostrar nombre/cédula en ticket y quitar posición en cola
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-02 23:53:39 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 c9e5744de4 feat(turnero): campo embarazada en recepción y badge en toma de muestras
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-02 23:48:03 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 8074f33b32 fix(turnero): eliminar duplicados en pantalla TV al llamar turno
callKey usaba id+llamado_at: si llamado_at llegaba null en el primer
poll y con valor en el segundo, se generaban dos claves distintas para
el mismo evento y el turno se anunciaba dos veces.

Cambia callKey a String(id) — cada turno se anuncia una sola vez
por sesión de pantalla, sin importar cambios en el timestamp.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-02 23:32:31 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 c4154b5b61 fix(turnero): deduplicar historial de llamados por turno en pantalla TV
El array 'llamados' acumulaba una entrada por cada lugar al que pasaba
un turno (recepción + toma de muestras). Al insertar un nuevo llamado
se elimina la entrada previa del mismo turno, dejando solo el último lugar.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-02 23:28:21 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 910edeb876 feat(turnero): botón de llamado por registro en lugar/toma de muestras
- Agrega botón campana por cada card en cola (solo turnos en_espera_lugar)
- Click en card abre ficha sin llamar; campana llama ese turno específico
- Llamar siguiente ahora también llama de verdad (antes solo abría ficha)
- Cards en_servicio mantienen indicador de estetoscopio sin botón

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-02 23:25:34 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 6adbcc3059 feat(turnero): modal completo de paciente en recepción
- Reemplaza window.open a lab_pacientes.php por modal Bootstrap inline
- Modal incluye campos completos: datos personales, contacto (con verificador WA), ubicación y EPS
- Botón Ver/Editar en paciente vinculado carga datos desde API y abre modal en modo edición
- Botón Cambiar desvincula y enfoca el buscador
- Al crear: seleccionarPaciente() auto-vincula al turno y cierra modal
- Al editar: actualiza labels en pantalla sin perder el turno activo

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-02 23:02:24 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 e90cfda3f3 feat(dashboard): accesos rápidos en dropdown por grupo, filtrado por rol
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-24 09:03:57 -05:00
Lizandro Guarnizo f724e2e94c Revert "fix(recepcion): mostrar nombre, cédula y celular en resultados de búsqueda de paciente"
This reverts commit 1af731ee44.
2026-06-24 08:54:17 -05:00
Lizandro Guarnizo 86be0669af Revert "fix(recepcion): reemplazar template literals anidados por concatenación compatible"
This reverts commit 0021af36a5.
2026-06-24 08:54:17 -05:00
Lizandro Guarnizo 3b1b7c9333 Revert "fix(sidebar): null-check en route de children para evitar TypeError en PHP 8.2"
This reverts commit ba27511414.
2026-06-24 08:54:17 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 ba27511414 fix(sidebar): null-check en route de children para evitar TypeError en PHP 8.2
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-24 08:52:26 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 0021af36a5 fix(recepcion): reemplazar template literals anidados por concatenación compatible
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-24 08:50:47 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 1af731ee44 fix(recepcion): mostrar nombre, cédula y celular en resultados de búsqueda de paciente
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-24 08:45:43 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 af584bcc26 feat(pacientes): verificar WhatsApp al ingresar teléfono, vincular user_id al guardar
fix(recepcion): mostrar nombre_completo en resultados de búsqueda de paciente

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-24 08:44:48 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 90baff34f9 fix(turnero): agregar sidebar toggle en vistas standalone (recepcion, lugar, configuracion)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-24 08:31:08 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 da594e0021 fix(dashboard): mostrar todos los escritorios y lugares sin agrupar ni limitar
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-24 08:28:06 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 5f0565eca2 feat(dashboard): quitar botón atrás, agregar accesos rápidos a recepción y lugares
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-24 08:24:36 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 0f07ee22c7 feat: dropdown destino muestra Toma de Muestras, Pediatría y Ginecología
Las 8 estaciones de muestras ya comparten grupo_id=1, así que se
agrupan en una sola opción 'Toma de Muestras'. Pediatría y Ginecología
tienen grupo_id=NULL y aparecen como destinos independientes.
Cualquier estación del grupo ve los turnos asignados a él.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-24 01:39:43 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 c0f99fe9bc feat: mostrar todos los lugares destino en dropdown de recepción
El dropdown solo mostraba el primer lugar tipo muestras (LIMIT 1).
Ahora carga todos los lugares activos tipo muestras, incluyendo
Pediatría y Ginecología que ya estaban en la BD.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-24 01:37:48 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 64f2e7c784 fix: recepcion_desk_id quedaba NULL en historial
Agregado selector de escritorio al abrir recepcion.php sin desk_id.
Si hay múltiples escritorios muestra un modal para elegir; si hay uno
solo se asigna automáticamente. Así recepcion_desk_id siempre queda
registrado en el turno y aparece correctamente en el historial.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-24 01:27:28 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 e80c1d180d fix: eliminar auto-llamado al siguiente turno tras pasar a lugar
La recepcionista debe llamar manualmente al siguiente turno.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-24 01:04:44 -05:00