Sprint 5. Etapa 1.
Some checks failed
/ Deploy to Cloudflare Pages (push) Has been cancelled

This commit is contained in:
2026-06-19 15:31:03 -03:00
parent 84daf82e65
commit a2763ab755
13 changed files with 612 additions and 80 deletions

249
sprints/sprint-5/BRIEF.md Normal file
View File

@@ -0,0 +1,249 @@
# 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