Files
Marcos Benítez a2763ab755
Some checks failed
/ Deploy to Cloudflare Pages (push) Has been cancelled
Sprint 5. Etapa 1.
2026-06-19 15:31:03 -03:00

250 lines
12 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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