# Feature Specification: Corrección de Bugs de Rendimiento y Deuda Técnica Frontend

**Feature Branch**: `010-correccion-bugs-rendimiento-frontend`

**Created**: 2026-07-04

**Status**: Draft

**Input**: Análisis de rendimiento frontend — 20 problemas identificados, ordenados por impacto descendente.

> **Constitution (I. Spec-First)**: Este spec MUST ser agnóstico de stack.
> No mencionar Laravel, React, Inertia.js ni rutas de código. El CÓMO va en `plan.md`.

---

## User Scenarios & Testing *(mandatory)*

### User Story 1 — El formulario de contratos no congela la UI al escribir (Priority: P1)

Como usuario comercial que rellena un contrato a diario, necesito que el formulario
responda de forma instantánea a cada pulsación de tecla, sin que el sistema reevalúe
partes de la pantalla que no tienen relación con el campo que estoy modificando.

**Why this priority**: Un formulario que "tarda" al escribir es el síntoma más visible
de los bucles de re-evaluación excesivos e impacta la productividad del equipo todos los días.

**Independent Test**: Acceder al formulario de contrato, seleccionar un cliente y después
escribir en cualquier campo de texto libre. La interfaz debe responder sin lag perceptible.
Se puede verificar con las herramientas de desarrollo del navegador comprobando que no se
produzca ninguna «long task» (>50 ms) durante la escritura normal.

**Acceptance Scenarios**:

1. **Given** un contrato abierto con un cliente ya seleccionado, **When** el usuario escribe
   en cualquier campo de texto (nombre fiscal, dirección, observaciones, etc.),
   **Then** cada carácter aparece de inmediato sin retrasos perceptibles y sin que
   sectores no editados parpadeen o vuelvan a dibujarse.

2. **Given** un contrato con ambos suministros activos (eléctrico y gas),
   **When** el usuario modifica un campo del suministro eléctrico,
   **Then** la sección de gas permanece estática; no se re-evalúa ni vuelve a renderizarse.

3. **Given** que el usuario selecciona un nuevo cliente con el selector de búsqueda,
   **When** la selección se confirma,
   **Then** los campos de datos fiscales (NIF/CIF y razón social) se actualizan
   exactamente una vez, sin ciclos de actualización adicionales.

---

### User Story 2 — El panel lateral de configuración no penaliza la carga inicial de ninguna página (Priority: P1)

Como usuario que navega entre páginas del backoffice, necesito que cada página cargue
rápidamente, sin que un panel de configuración visual (temas, colores) que rara vez uso
bloquee la descarga del contenido principal.

**Why this priority**: El panel de configuración de temas pesa ~100 KB y está incluido
síncronamente en el punto de entrada de todas las páginas. Su impacto se multiplica en
cada navegación.

**Independent Test**: Abrir cualquier página del backoffice midiendo el tiempo hasta que
los elementos de la página sean interactivos. El panel de configuración puede aparecer
después de que el contenido principal sea visible.

**Acceptance Scenarios**:

1. **Given** que el usuario abre cualquier página, **When** se mide el tiempo hasta
   contenido interactivo, **Then** el panel de configuración de temas NO bloquea ese tiempo;
   su carga es diferida.

2. **Given** que el usuario nunca abre el panel de configuración de temas,
   **When** navega durante toda la sesión,
   **Then** el código de ese panel no se ejecuta ni consume memoria de forma activa.

---

### User Story 3 — El layout principal no acumula escuchadores de eventos entre páginas (Priority: P1)

Como usuario que navega entre varias secciones del backoffice durante su jornada, necesito
que la aplicación no degrade su rendimiento ni consuma más memoria a medida que navego,
independientemente del número de páginas visitadas.

**Why this priority**: La acumulación de escuchadores sin limpiar es la causa de que la app
pueda volverse progresivamente lenta o inestable en sesiones largas.

**Independent Test**: Abrir las herramientas de desarrollo del navegador en la pestaña de
memoria/performance, navegar entre 10 páginas distintas y verificar que el número de
escuchadores activos en el DOM no crece de forma indefinida.

**Acceptance Scenarios**:

1. **Given** que el usuario navega entre 10 páginas distintas,
   **When** se audita el número de event listeners activos en el DOM,
   **Then** el conteo permanece estable (no crece en proporción al número de navegaciones).

2. **Given** que el usuario redimensiona la ventana del navegador,
   **When** ha navegado por múltiples páginas,
   **Then** el sistema responde con un único manejador de redimensionado, sin ejecutar
   múltiples callbacks acumulados.

---

### User Story 4 — Los datos de tarifas se cargan una sola vez por sesión (Priority: P2)

Como usuario que crea varios contratos seguidos, necesito que la app no repita peticiones
de red para obtener catálogos de tarifas que ya obtuvo previamente y que no han cambiado.

**Why this priority**: Las peticiones de red duplicadas e innecesarias consumen ancho de
banda del servidor y alargan el tiempo de carga del formulario al reabrir contratos.

**Independent Test**: Abrir el formulario de contratos con suministros eléctrico y gas
activos, registrar las peticiones de red realizadas en las herramientas del navegador,
cerrar y volver a abrir el formulario; el catálogo de tarifas no debe pedirse de nuevo.

**Acceptance Scenarios**:

1. **Given** que el usuario abre el formulario de contratos por primera vez,
   **When** ambos suministros (eléctrico y gas) están activos,
   **Then** se realizan como máximo dos peticiones de catálogo (una por tipo de suministro),
   no cuatro ni más.

2. **Given** que el catálogo de tarifas ya se obtuvo en la misma sesión,
   **When** el usuario navega a otro contrato y vuelve,
   **Then** el catálogo se sirve desde caché local, sin nueva petición de red.

---

### User Story 5 — El selector de cliente no pisa datos de peticiones anteriores al cambiar rápidamente (Priority: P2)

Como usuario que busca clientes con agilidad, necesito que al cambiar el cliente
seleccionado mientras una petición anterior todavía está en curso, los datos que aparecen
en pantalla siempre correspondan al cliente que he seleccionado por último, nunca a uno previo.

**Why this priority**: Un resultado de una petición antigua que sobrescribe el estado actual
genera inconsistencias silenciosas que el usuario puede no detectar.

**Independent Test**: Seleccionar el cliente A, inmediatamente seleccionar el cliente B
antes de que la petición de CUPS del cliente A haya terminado; verificar que los CUPS
mostrados pertenecen al cliente B.

**Acceptance Scenarios**:

1. **Given** que el usuario selecciona el cliente A y su petición de datos está en vuelo,
   **When** el usuario selecciona el cliente B antes de que la petición A responda,
   **Then** solo los datos del cliente B aparecen en pantalla; los datos de la petición A
   se descartan automáticamente.

---

### User Story 6 — La consola del navegador no muestra mensajes de diagnóstico en producción (Priority: P3)

Como desarrollador responsable de la calidad del producto, necesito que en el entorno de
producción no se impriman mensajes de diagnóstico en la consola del navegador, para evitar
exponer información interna y no degradar el rendimiento de serialización de objetos de datos.

**Why this priority**: Los mensajes de diagnóstico en producción pueden serializar objetos
grandes (listas de clientes completas) en el hilo principal, causando micro-bloqueos.
Adicionalmente, exponen detalles de implementación.

**Independent Test**: Acceder a la aplicación en producción o staging, abrir la consola del
navegador y navegar por la sección de contratos y clientes. La consola no debe mostrar
ningún mensaje generado por la aplicación.

**Acceptance Scenarios**:

1. **Given** que el usuario navega por contratos en producción,
   **When** se abre la consola del navegador,
   **Then** no aparece ningún mensaje de diagnóstico de la aplicación
   (búsquedas de clientes, carga de CUPS, etc.).

2. **Given** que la aplicación se ejecuta en entorno de desarrollo,
   **When** el desarrollador activa los mensajes de diagnóstico,
   **Then** solo se emiten en ese entorno; nunca en producción.

---

### User Story 7 — La dependencia de diseño de terceros no penaliza a usuarios de contratos multi-punto (Priority: P3)

Como usuario que utiliza la vista de gestión de contratos multi-punto, necesito que la
página cargue ágilmente aunque incluya un cuadro de diálogo de edición, sin que se
descargue una librería completa de componentes de UI (~300 KB) solo para ese propósito.

**Why this priority**: La eliminación de una dependencia pesada de terceros usada en un
único lugar reduce significativamente el tamaño del paquete principal.

**Independent Test**: Abrir la página de contratos multi-punto y medir el tamaño total de
JavaScript descargado. Verificar que el cuadro de diálogo de edición sigue funcionando
correctamente con la solución alternativa.

**Acceptance Scenarios**:

1. **Given** que el usuario accede a la página de contratos multi-punto,
   **When** se carga la página,
   **Then** la librería de UI de terceros pesada no se incluye en el paquete descargado.

2. **Given** que el usuario hace clic en "editar" dentro de la tabla de CUPS,
   **When** se activa la acción,
   **Then** aparece un cuadro de diálogo de edición completamente funcional,
   empleando la librería de componentes ya disponible en el resto del proyecto.

---

### Edge Cases

- ¿Qué ocurre si la caché de tarifas contiene datos desactualizados (la tarifa fue eliminada
  en el backend entre sesiones)? El sistema debe poder invalidar la caché adecuadamente.
- ¿Cómo se comporta el formulario si el usuario pierde conexión mientras una petición de
  CUPS está en vuelo? Se debe comunicar el error sin dejar el formulario en estado inconsistente.
- ¿Qué pasa si el usuario abre simultáneamente el mismo contrato en dos pestañas distintas?
  Las correcciones de rendimiento no deben introducir problemas de estado compartido inesperados.

---

## Requirements *(mandatory)*

### Functional Requirements

- **FR-001**: El sistema MUST garantizar que la selección de un cliente actualiza los
  campos dependientes exactamente una vez, independientemente de cuántos otros campos
  del formulario hayan sido modificados.

- **FR-002**: El sistema MUST servir los catálogos de tarifas (eléctrica y gas) desde
  una caché local durante la sesión activa; una misma sesión no debe realizar más de
  una petición por tipo de catálogo.

- **FR-003**: El sistema MUST asegurar que, al cerrar o desmontar cualquier sección de
  la pantalla, todos los escuchadores de eventos DOM registrados por esa sección sean
  eliminados limpiamente.

- **FR-004**: El sistema MUST garantizar que, al navegar entre páginas a lo largo de
  una sesión de trabajo prolongada, el número de escuchadores de eventos activos
  permanezca estable y no crezca de forma acumulativa.

- **FR-005**: El sistema MUST diferir la carga del panel de configuración de temas de
  forma que no bloquee la descarga e interactividad del contenido principal de ninguna página.

- **FR-006**: Los mensajes de diagnóstico (logs de consola) generados por la aplicación
  MUST estar completamente silenciados en el entorno de producción.

- **FR-007**: Cuando el usuario cambia la selección de cliente mientras una petición de
  datos asociada está en curso, el sistema MUST cancelar la petición anterior y solo
  procesar la respuesta de la petición más reciente.

- **FR-008**: El formulario de contratos multi-punto MUST proporcionar la funcionalidad
  de cuadro de diálogo de edición de CUPS sin depender de la librería de UI de terceros
  actualmente importada solo para ese fin.

- **FR-009**: Los selectores de memoización de estado global MUST ser instanciados fuera
  del ciclo de vida de los componentes que los consumen, de forma que su caché sea
  efectiva a lo largo de múltiples renderizados.

- **FR-010**: El sistema MUST garantizar que el layout global no dispare actualizaciones
  de estado redundantes en cada navegación cuando los valores a establecer ya coinciden
  con los almacenados actualmente.

### Key Entities

- **Formulario de Contrato**: Conjunto de campos que el usuario rellena para registrar un
  contrato. Puede incluir secciones de suministro eléctrico y/o gas de forma independiente.
- **Catálogos (Tarifas)**: Conjuntos de datos de referencia utilizados para completar el
  contrato (tarifas eléctricas, tarifas de gas). Son estables durante una sesión de trabajo.
- **Panel de Configuración de Temas**: Componente auxiliar para personalizar el aspecto
  visual de la aplicación. Es de uso infrecuente y no debe condicionar la carga principal.
- **Escuchador de Eventos**: Mecanismo de la interfaz que reacciona a interacciones del
  usuario (clics, redimensionado, etc.). Deben liberarse cuando el componente que los
  registra desaparece de pantalla.
- **Selector de Estado Global**: Función que extrae y memoiza un subconjunto del estado
  compartido de la aplicación. Su efectividad depende de su instanciación fuera del ciclo
  de renderizado.

---

## Success Criteria *(mandatory)*

### Measurable Outcomes

- **SC-001**: Al escribir en cualquier campo del formulario de contratos, ninguna «long task»
  (tarea que bloquea el hilo principal más de 50 ms) debe registrarse en las herramientas
  de perfilado del navegador.

- **SC-002**: El número de peticiones HTTP para catálogos de tarifas al abrir el formulario
  de contratos (con ambos suministros activos) NO debe superar 2 (una eléctrica, una de gas);
  en visitas subsecuentes dentro de la misma sesión, debe ser 0.

- **SC-003**: Tras navegar por 10 páginas distintas del backoffice, el número de event
  listeners activos registrado por las herramientas del navegador no debe haber crecido
  respecto al estado inicial de la sesión.

- **SC-004**: El tamaño del paquete JavaScript descargado para la página de contratos
  multi-punto debe reducirse en al menos 250 KB (gzip) respecto al estado actual
  al eliminar la dependencia de UI de terceros.

- **SC-005**: La consola del navegador en producción debe mostrar exactamente 0 mensajes
  generados por la lógica de la aplicación durante un flujo estándar de creación de contrato.

- **SC-006**: El tiempo hasta la primera interacción (First Input Delay / INP) en cualquier
  página del backoffice no debe superar los 100 ms en condiciones de red estándar.

---

## Assumptions

- Se asume que la mayoría de los usuarios opera en sesiones continuas de más de 30 minutos,
  haciendo que la acumulación de escuchadores sea un problema real y no teórico.
- Se asume que los catálogos de tarifas no cambian con frecuencia durante una jornada de
  trabajo; una caché de sesión es suficiente sin necesidad de invalidación automática por tiempo.
- Se asume que el panel de configuración de temas es de uso ocasional; diferir su carga no
  afectará el flujo de trabajo principal de ningún usuario.
- El alcance de esta feature es exclusivamente la capa de presentación (interfaz de usuario);
  la lógica de negocio, los modelos y las reglas de validación existentes se mantienen intactos.
- Las reglas de accesibilidad (WCAG) y el sistema de tabs definidos en los constraints del
  proyecto no se ven afectados por estas correcciones.
- La solución al cuadro de diálogo en contratos multi-punto MUST utilizar el componente de
  diálogo/modal ya disponible en el sistema de diseño actual del proyecto.
