# Sprint 5 — BRIEF > InQ ROI — Sprint de UX polish + quick wins > Inicio: 2026-06-19 > Foco: experiencia del usuario, mejoras visuales, features de alto impacto/bajo esfuerzo --- ## Contexto Sprint 4 fue un refactor arquitectural completo (IndexedDB → Supabase, auth, recursos globales). El producto está estable y en uso interno. Sprint 5 vuelve al rebalanceo normal: **60-70% features de producto, 10-15% infra/deuda técnica**. Sin features de gran escala (historial de simulaciones, compartir con guests). Solo mejoras que suman experiencia real. --- ## Etapas ### Etapa 1 — Fix crítico: workspace loading **Problema:** Al navegar desde la biblioteca a un proceso, el workspace se queda cargando indefinidamente. La pantalla solo carga correctamente después de refrescar la página manualmente. **Diagnóstico a confirmar:** probable race condition entre `useEffect` de carga del proceso y disponibilidad del usuario autenticado. El `processId` llega por route params antes de que el store tenga el usuario listo, y el query a Supabase se dispara con un contexto incompleto o se omite silenciosamente. **Alcance:** - Diagnosticar el flujo completo: `WorkspacePage` → `useProcess` hook → `process-repo.getById()` → render - Identificar dónde el guard de auth interactúa mal con la carga del proceso - Corregir sin introducir flickering ni doble render - El workspace debe cargar al primer intento, sin refresh **Criterios:** - [ ] Navegar desde la biblioteca a cualquier proceso carga el workspace en el primer intento - [ ] Sin errores en consola durante la navegación - [ ] El estado de loading muestra un skeleton o spinner visible (no pantalla en blanco) - [ ] Tests Vitest verdes (sin regresiones) - [ ] Validación visual del director --- ### Etapa 2 — Fix + mejora: zoom BPMN **Problema:** El botón de fit-viewport actual posiciona el diagrama pegado al borde superior, sin padding. Además no hay controles de zoom explícitos. **Alcance:** **Fix fit-viewport:** - Corregir `canvas.zoom('fit-viewport')` para que el diagrama quede centrado con padding uniforme (sugerido: 40px en todos los bordes) - bpmn-js expone `canvas.zoom('fit-viewport', true)` y opciones de padding — usar la API correcta **Controles de zoom (versión wow):** - Botón "+" (zoom in) y "−" (zoom out) en el canvas - Botón de reset/fit (ícono de encuadre) — corrige el bug actual + sirve de reset - Posición: esquina inferior derecha del canvas, sin tapar el diagrama - Estilo: botones cuadrados de 32px, fondo blanco semitransparente, borde sutil, ícono Tabler - No interferir con el zoom por scroll/pinch que ya funciona **Íconos sugeridos:** `ti-zoom-in`, `ti-zoom-out`, `ti-focus-2` (para fit) **Criterios:** - [ ] Fit-viewport centra el diagrama con padding uniforme (no clip al borde) - [ ] Botones +/−/fit visibles en el canvas - [ ] +/− hacen zoom in/out incremental (factor 1.2x por click) - [ ] Fit resetea a la vista centrada - [ ] Los controles no aparecen encima de los paneles laterales - [ ] Tests Vitest verdes - [ ] Validación visual del director --- ### Etapa 3 — Heatmap con gradiente continuo + toggle de modo **Problema actual:** el heatmap usa 3 buckets discretos (verde/amarillo/rojo) con colores fijos. Sin matices intermedios, sin gradiente. **Alcance:** **Escala de color continua:** Reemplazar la lógica de 3 buckets por una función de interpolación de 4 stops: ``` 0.0 → #10b981 (verde) 0.33 → #f59e0b (amarillo) 0.67 → #f97316 (naranja) 1.0 → #ef4444 (rojo vivo) ``` Implementar en `src/lib/colors.ts` como `intensityToColor(normalized: number): string` usando interpolación lineal en RGB entre stops consecutivos. **Dos modos de normalización:** `percentile` (default): cada actividad se posiciona según su rango relativo entre todas las actividades del proceso. No sufre distorsión por outliers. `minmax`: `(costo - mínimo) / (máximo - mínimo)`. Refleja valores absolutos pero se comprime con outliers. **Persistencia del modo:** guardar la preferencia en `localStorage` (key: `heatmap-mode`). Default: `percentile`. **Toggle en UI:** - Un toggle compacto en el panel del reporte o en la barra de herramientas del workspace - Label: "Escala de color: Relativa (percentil) / Absoluta (min-max)" - Tamaño pequeño, no protagonista — es una preferencia técnica, no el flujo principal **Dónde aplica el heatmap:** - Nodos del canvas BPMN (overlay de color en actividades) - Tabla del reporte web (fondo de celdas de costo) - PDF: usa los mismos valores calculados pero sin el toggle (renderiza el modo activo al exportar) **Recalibrar E2E de píxeles:** los tests que verifican colores del canvas BPMN necesitan actualizar sus umbrales/valores esperados para la nueva escala. Identificar los specs afectados y corregirlos. **Criterios:** - [ ] El gradiente muestra transiciones suaves (no saltos abruptos de verde a rojo) - [ ] Modo percentil: actividades distribuidas a lo largo de la escala incluso con outliers - [ ] Modo min-max: outlier extremo domina la escala (comportamiento esperado) - [ ] Toggle funciona y persiste entre sesiones - [ ] PDF exporta con el modo activo al momento de la exportación - [ ] Tests E2E de heatmap recalibrados y verdes - [ ] Tests Vitest verdes (nueva función `intensityToColor` con tests unitarios) - [ ] Validación visual del director --- ### Etapa 4 — Ver procesos por recurso + campo empresa en perfil Dos features pequeñas en una etapa. **Feature A — Ver procesos asociados a un recurso:** En `ResourceCatalogPage` (`/recursos`), cada fila de la tabla debe mostrar cuántos procesos usan ese recurso. - Nueva columna "Procesos" en la tabla: muestra un badge con el número (ej: `3 procesos`) - Si es 0: muestra "—" con color muted - Si es > 0: el badge es clickeable y expande una lista de los procesos (nombre + link directo al workspace) - La expansión puede ser inline (fila expandible) o un popover/tooltip al hover - Dato: query a `activity_resource_assignments` agrupado por `resource_id`, con JOIN a `processes` para el nombre - Esto complementa la protección de borrado existente — ahora el usuario ve *qué* proceso usa el recurso antes de intentar eliminarlo **Feature B — Campo empresa en perfil de usuario:** - Nueva columna `company text` en tabla `users` de Supabase (nullable, sin default) - Migración: `ALTER TABLE users ADD COLUMN IF NOT EXISTS company text;` - UI: en el dropdown del AppHeader (donde aparece nombre y email), agregar una línea con la empresa si está definida - Edición: agregar un botón "Editar perfil" en el dropdown que abre un Sheet con campos editables: nombre y empresa (email es de solo lectura — viene de Google OAuth) - La empresa se muestra también en la tabla de la biblioteca si hay espacio (opcional, decidir con validación visual) **Criterios:** - [ ] Columna "Procesos" visible en `/recursos` con conteo correcto - [ ] Click en el badge con conteo > 0 muestra la lista de procesos con links funcionales - [ ] Columna `company` creada en Supabase sin romper datos existentes - [ ] El dropdown del AppHeader muestra la empresa si está definida - [ ] Sheet de edición de perfil guarda nombre y empresa en Supabase - [ ] Tests Vitest verdes - [ ] Validación visual del director --- ### Etapa 5 — Columna "Eficiencia de automatización" **Contexto:** la tabla de comparación (ComparisonTable) hoy muestra costo AS-IS, costo TO-BE, ahorro y probabilidad. Le falta una métrica que permita *rankear* actividades por valor de automatización — no solo quién ahorra más en valor absoluto, sino qué actividades ofrecen el mejor retorno por peso invertido. **Métrica:** ``` eficiencia = (ahorro$ × probabilidad) / tiempo_ejecucion_minutos ``` Alternativa si no hay inversión por actividad: usar `ahorro$ × probabilidad` como proxy simple (ahorro esperado ponderado). Decidir en implementación cuál es más interpretable para un consultor. **Alcance:** - Nueva columna "Eficiencia" en `ComparisonTable` del reporte web - Valor = ahorro esperado ponderado por probabilidad de automatización - Ordenar la tabla por esta columna por defecto (mayor eficiencia primero) — con posibilidad de cambiar el orden haciendo click en el header - Versión wow: barra de progreso inline de ancho relativo (la actividad más eficiente = 100%, las demás proporcionales). Mismo estilo que las barras del heatmap - Tooltip en el header de la columna explicando la fórmula - Si `isAutomatable = false`: mostrar "—" (em dash), igual que columna Ahorro **En PDF:** - Incluir la columna en la tabla de comparación del PDF si entra sin truncamiento - Si no entra, omitirla (el PDF ya es denso) — decidir en validación visual **Criterios:** - [ ] Columna visible en el reporte web con valores correctos - [ ] Actividades no automatizables muestran "—" - [ ] Orden por defecto: mayor eficiencia primero - [ ] Click en header reordena la tabla - [ ] Barra de progreso proporcional visible - [ ] Tooltip en header con explicación de la fórmula - [ ] Cálculo tiene tests unitarios en Vitest - [ ] Validación visual del director --- ### Etapa 6 — Limpieza técnica **6a — E2E: reescribir specs Dexie → Supabase** - `export-pdf.spec.ts` y `validate-dod-etapa4.spec.ts` inyectan datos vía Dexie/IndexedDB, que fue eliminado en Sprint 4 - Reescribir fixtures para usar el test user `martin.bernal@inquality.com.py` via Supabase - Limpiar datos de test después de cada corrida (usar `afterAll` con `service_role_key`) **6b — E2E: fix flakiness "Guardar cambios"** - 3 specs fallan intermitentemente por timing: Playwright no encuentra el botón en estado habilitado - Fix: `await page.waitForSelector('[data-testid="save-btn"]:not([disabled])')` o equivalente - Agregar `data-testid="save-btn"` al botón en `ActivityPanel.tsx` si no existe **6c — Limpieza de BACKLOG y documentación** - Marcar como `[RESOLVED]` en `BACKLOG.md`: "Indicador de simulación desactualizada", "Edición inline de proceso/cliente", "Controles de zoom BPMN" (resueltos en Sprints 2/3 y Etapa 2 de este sprint) - Actualizar `STRATEGIC_DIRECTION.md` con cierre de Sprint 4 (pendiente desde sesión anterior) - Vaciar `OBSERVATIONS.md` y preparar encabezado para Sprint 5 - Marcar como `[RESOLVED]` en `TECH_DEBT.md` los ítems resueltos en esta etapa **Criterios:** - [ ] `export-pdf.spec.ts` y `validate-dod-etapa4.spec.ts` verdes en CI con Supabase real - [ ] 3 specs flaky del botón "Guardar" estables - [ ] `npm run test` — mantener o superar conteo anterior de Vitest - [ ] `npx playwright test` — 0 specs permanentemente rojos - [ ] BACKLOG, TECH_DEBT, STRATEGIC_DIRECTION y OBSERVATIONS actualizados - [ ] Validación del director sobre el estado de los archivos --- ## Distribución del sprint | Área | Etapas | % | |---|---|---| | Features de producto / UX | 1, 2, 3, 4, 5 | ~83% | | Infraestructura / deuda técnica | 6 | ~17% | | PDF | (toca en Etapa 3 y 5, sin etapa dedicada) | — | Dentro del rango válido del rebalanceo (60-70% web, 10-15% infra). PDF sin etapa dedicada — aceptable este sprint dado que Sprint 4 no tocó PDF. --- ## Qué NO entra en Sprint 5 - Historial de simulaciones / múltiples simulaciones por proceso - Implementación real de `setShared()` (diferida hasta primer guest) - Keycloak / migración de auth - Modo offline / PWA - i18n - Export Excel - Gateway OR inclusivo --- ## Definition of Done del sprint - [ ] 6 etapas con OK formal del director (validación visual de cada una) - [ ] `npm run build` limpio - [ ] `npm run lint` sin warnings - [ ] Tests Vitest: mantener o superar conteo de Sprint 4 - [ ] Tests E2E: 0 specs permanentemente rojos - [ ] CHECKLIST_ENTREGA.md ejecutado antes de cada entrega de etapa - [ ] OBSERVATIONS.md con hallazgos del sprint al cierre - [ ] STRATEGIC_DIRECTION.md actualizado con Sprint 4 y 5