Commit Graph
1196 Commits
Author SHA1 Message Date
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
LizandroandClaude Sonnet 4.6 828681ffd6 feat(turnero): cambio de estado y reapertura de turnos desde historial
Agrega botón de cambio de estado en cada fila del historial de turnos.
Admins (sin role_id) pueden reabrir turnos finalizados/ausentes/cancelados.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-28 01:47:11 +00: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