diff --git a/BACKLOG.md b/BACKLOG.md index d0f326d..960e8b6 100644 --- a/BACKLOG.md +++ b/BACKLOG.md @@ -20,8 +20,14 @@ Al cerrar un sprint, los ítems `[SPRINT-N]` se promocionan a `[SPRINT-(N+1)]` o ## Features de alta prioridad -### [BACKLOG] Indicador de simulación desactualizada -Cuando el usuario guarda cambios en una actividad (costos, recursos, automatización) pero no vuelve a simular, el reporte muestra resultados del estado anterior sin ningún aviso. Agregar un indicador sutil ("Hay cambios sin simular" o badge en el botón Simular) que desaparezca al ejecutar la simulación. Detectado en uso real durante Sprint 2 — genera confusión al no ver los nuevos costos reflejados en el reporte. +### [BACKLOG] Ver procesos asociados a un recurso + navegar a ellos +Desde el catálogo de recursos (`/recursos`), cada recurso debería mostrar cuántos procesos lo usan y permitir navegar a esos procesos directamente. Detectado en validación visual Sprint 4 Etapa 8. Valor: evitar eliminar accidentalmente recursos en uso (hoy hay protección, pero sin visibilidad de qué proceso los usa) y facilitar auditoría del catálogo. + +### [BACKLOG] Campo "empresa" en perfil de usuario +El perfil del usuario hoy muestra nombre y email. Agregar campo `company` o `organization` en la tabla `users` para mostrar a qué empresa pertenece el consultor. Útil cuando haya múltiples consultores de distintas organizaciones (guests). Detectado en validación visual Sprint 4 Etapa 8. + +### [RESOLVED] Indicador de simulación desactualizada +Implementado en Sprint 3. Confirmado funcionando en validación visual Sprint 4 (muestra "Hay cambios sin simular" junto al botón Simular). Cerrado Sprint 5 planning (2026-06-19). ### [BACKLOG] Múltiples simulaciones por proceso Historial de simulaciones con comparación de escenarios. @@ -29,8 +35,8 @@ Historial de simulaciones con comparación de escenarios. ### [BACKLOG] Recursos con costo por hora Link actividad ↔ recurso (rol, persona, sistema) con cálculo automático del costo agregado. -### [BACKLOG] Edición del nombre de proceso y cliente desde el workspace -Hoy requiere ir a configuración global. Mover edición inline al workspace. +### [RESOLVED] Edición del nombre de proceso y cliente desde el workspace +Implementado en Sprint 2. Confirmado funcionando en validación visual Sprint 4. Cerrado Sprint 5 planning (2026-06-19). ### [BACKLOG] Gateway OR inclusivo nativo Actualmente se simula como XOR. Implementar propagación OR real con probabilidades independientes. @@ -57,8 +63,8 @@ Manifest + service worker para uso offline. ### [BACKLOG] Zoom/pan del heatmap desde el reporte Hoy es visualización estática. Permitir interacción. -### [BACKLOG] Controles de zoom en canvas BPMN (fit-to-screen / reset zoom) -El canvas puede quedar muy zoomed out sin forma de volver al estado inicial. bpmn-js expone `canvas.zoom('fit-viewport')` — agregar botón "Encuadrar diagrama" o equivalente. Detectado en validación visual Sprint 2 Etapa 3. +### [SPRINT-5] Controles de zoom en canvas BPMN (fit-to-screen / zoom-in / zoom-out) +Fit-viewport implementado en Sprint 3 pero con bug: clip al borde superior sin centering. Sprint 5 Etapa 2 corrige el bug y agrega controles de zoom-in/zoom-out explícitos. ### [BACKLOG] Detalle de recursos por actividad en el PDF Tabla de costos por recurso asignado dentro de cada actividad. @@ -66,15 +72,15 @@ Tabla de costos por recurso asignado dentro de cada actividad. ### [BACKLOG] Importación por URL Cargar BPMN desde enlace directo (GitHub, Confluence, etc.). -### [BACKLOG] Columna "Eficiencia de automatización" en ComparisonTable -Métrica = `(ahorro$ × probabilidad) / inversión-prorrateada`. Permite rankear actividades por "qué tan valiosa es automatizar cada una". +### [SPRINT-5] Columna "Eficiencia de automatización" en ComparisonTable +Métrica = `ahorro$ × probabilidad` (ahorro esperado ponderado). Permite rankear actividades por valor de automatización. Ver Sprint 5 Etapa 5. --- ## Features Fase 2 (versión 2.0 del producto) -### [SPRINT-4] Login y multi-tenant -Persistencia en la nube, colaboración entre múltiples usuarios. Promovido a Sprint 4 — ver `sprints/sprint-4/BRIEF.md`. Implementación: Supabase + Google Workspace OAuth, PostgreSQL, modelo de privacidad private/team. +### [RESOLVED] Login y multi-tenant +Implementado en Sprint 4. Supabase + Google Workspace OAuth, PostgreSQL, RLS, catálogo de recursos global, identidad en UI. Ver `sprints/sprint-4/BRIEF.md`. Cerrado 2026-05-xx. ### [FASE-2] Versionado de procesos Historial de cambios con diff visual. diff --git a/OBSERVATIONS.md b/OBSERVATIONS.md index 1670fac..4bd0953 100644 --- a/OBSERVATIONS.md +++ b/OBSERVATIONS.md @@ -1,9 +1,9 @@ -# OBSERVATIONS — Sprint 4 +# OBSERVATIONS — Sprint 5 > Hallazgos descubiertos durante el sprint actual. > Este archivo se vacía al cerrar el sprint: los hallazgos se promocionan a TECH_DEBT.md o BACKLOG.md, o quedan resueltos. > -> **Sprint 2 cerrado el 2026-05-20.** Ver cierre en `methodology/CASE_STUDIES/inq-roi-sprint-2.md`. +> **Sprint 4 cerrado el 2026-06-18.** Observaciones archivadas — ítems migrados a BACKLOG.md y TECH_DEBT.md. --- @@ -18,29 +18,13 @@ ## Hallazgos activos -### [DEFERRED-BACKLOG] Compartir proceso — implementación real diferida hasta primer guest - -**Etapa:** 5 (UI placeholder), decisión confirmada post-Etapa 7B -**Estado actual:** `setShared()` en `process-repo.ts` es no-op con comentario explicativo. El botón "Compartir con equipo" en LibraryPage muestra toast de confirmación pero no escribe en `process_access`. Todos los usuarios actuales son `platform_admin` y ven todo por bypass de RLS — el sharing no tiene efecto observable hoy. -**Schema listo:** tabla `process_access (process_id, grantee_type, grantee_id, permission)` + `user_groups` + `group_memberships` + RLS policies que ya usan `process_access` para usuarios no-admin. -**Trigger para implementar:** cuando se agregue el primer usuario `guest` (cliente externo o consultor externo) a la plataforma. -**Pendiente al implementar:** (1) UX de selección — ¿compartir con usuario específico, grupo, o "todo el equipo"? (2) implementación real de `setShared()` con escritura en `process_access`, (3) UI de gestión de grupos. +*(vacío al inicio del sprint)* --- ## Hallazgos resueltos durante el sprint -### [RESOLVED] Web Locks API deadlock en supabase-js v2 — biblioteca colgaba indefinidamente - -**Etapa:** 5B (deploy Dokploy) -**Síntoma:** Biblioteca mostraba 0 procesos. Network tab sin llamadas a Supabase después de la primera carga. Import con timeout de 20s. Aparentemente aleatorio — "la primera vez funciona, después no". -**Diagnóstico inicial (asesor):** Race condition entre inicialización de sesión async y fetch de procesos. Correcto en dirección pero incompleto en causa raíz. -**Causa raíz real (Claude Code):** `supabase-js` v2 usa `navigator.locks` (Web Locks API) para coordinar refresh de sesión entre pestañas. Un lock atascado de una pestaña cerrada abruptamente bloqueaba para siempre cualquier llamada futura a `getSession()` o queries. El cliente quedaba en espera indefinida del lock — sin error, sin timeout. -**Fix aplicado:** -- `src/lib/supabase.ts`: cambiar a `processLock` (coordinación en memoria, sin Web Locks API) -- Timeouts con error visible y botón "Reintentar" como red de contención -**Lección para el método:** Síntomas de race condition (funciona la primera vez, no las siguientes) pueden esconder problemas de locks de sistema. Si el network tab no muestra ninguna llamada cuando debería haberlas, sospechar bloqueo a nivel de cliente antes del request, no solo timing de React state. -**Estado:** RESOLVED — Etapas 5 y 5B con OK formal del director (2026-06-18) +*(vacío)* --- diff --git a/TECH_DEBT.md b/TECH_DEBT.md index 18f6d28..5a55a62 100644 --- a/TECH_DEBT.md +++ b/TECH_DEBT.md @@ -47,6 +47,18 @@ en los 3 PDFs. Si hay truncamiento, evaluar: ## Deuda media — afecta DX o evolución del producto +### [BACKLOG] E2E specs con Dexie directo — reescribir para Supabase +**Estado actual:** `export-pdf.spec.ts` y `validate-dod-etapa4.spec.ts` inyectan datos vía Dexie/IndexedDB directamente. Dexie fue eliminado en Sprint 4 Etapa 4 — estos specs fallan en cualquier entorno donde no exista la DB local del sprint anterior. +**Impacto:** 2 specs permanentemente rojos. No bloquean build ni deploy, pero degradan la señal de la suite E2E. +**Solución propuesta:** reescribir los fixtures de datos para insertar vía Supabase (usando el test user `martin.bernal@inquality.com.py` y el `service_role_key` en el setup de Playwright). Limpiar datos de test después de cada corrida. +**Costo:** medio. Requiere diseño cuidadoso de fixtures para no interferir con datos de producción. + +### [BACKLOG] E2E flakiness — "Guardar cambios" button timing en ActivityPanel +**Estado actual:** 3 specs de PDF fallan intermitentemente porque Playwright no encuentra el botón "Guardar cambios" en estado habilitado. Validación manual confirma que el botón SÍ funciona correctamente — es un issue de timing en el test, no un bug real. +**Causa probable:** el test hace click en la actividad y espera el botón sin suficiente guard sobre el estado inicial de `isDirty`. +**Solución propuesta:** agregar `await page.waitForSelector('[data-testid="save-btn"]:not([disabled])')` o un `data-testid` explícito que permita espera determinística. +**Costo:** bajo. Una línea por spec afectado. + ### [INVESTIGATING] Dexie live queries (sincronización store ↔ db) **Estado actual:** la invalidación del resultado de simulación se garantiza vía ESLint rule (`no-restricted-imports`) + convención: todas las mutaciones pasan por `useProcessStore`. diff --git a/cowork/InQ ROI - Simulador de procesos/CLAUDE.md b/cowork/InQ ROI - Simulador de procesos/CLAUDE.md index faaf831..38c58f1 100644 --- a/cowork/InQ ROI - Simulador de procesos/CLAUDE.md +++ b/cowork/InQ ROI - Simulador de procesos/CLAUDE.md @@ -9,11 +9,13 @@ ## Estado actual del proyecto -**Sprint:** 4 (planificado — BRIEF listo en `sprints/sprint-4/BRIEF.md`) +**Sprint:** 5 (en curso — BRIEF listo en `sprints/sprint-5/BRIEF.md`) **Fase:** 1 — ROI y escenarios de automatización -**Deploy activo:** https://process-cost-platform.pages.dev +**Deploy activo:** https://inq-roi.inqualityhq.com (Dokploy) · https://process-cost-platform.pages.dev **Usuario objetivo actual:** equipo de InQuality — uso interno en talleres y con clientes en curso +**Sprint 4 cerrado el 2026-06-18.** Entregó: auth Supabase + Google Workspace OAuth, migración IndexedDB→PostgreSQL, RLS con `is_platform_admin()`, catálogo global de recursos (sin `processId`), identidad en UI (avatar/dropdown AppHeader, "Actualizado por"), ResourceCatalogPage + ResourceFormSheet, fix Web Locks API deadlock (`processLock`), fix `.maybeSingle()` en simulation-repo, fix `formatCostPerHour()` precisión dinámica, fix Google OAuth `prompt: 'select_account'`, catálogo con contexto de proceso (banner + query param `fromProcess`), batería E2E Sprint 4 + `docs/CHECKLIST_ENTREGA.md`. ~550+ tests. + **Sprint 3 cerrado el 2026-05-27.** Entregó: biblioteca de procesos con grupos/tags/búsqueda (Dexie v4), indicador stale de simulación, zoom BPMN fit-viewport, recursos TO-BE en escenario automatizado (motor + UI + reporte + PDF), branding InQ ROI en todas las páginas (AppHeader + favicon + logos), kit de socialización del equipo (guía de usuario, guía de pre-venta, presentación PPTX). 561 tests. **Sprint 2 cerrado el 2026-05-20.** Entregó: modelo de recursos `utilizationMinutes + units` (migración Dexie v3), ActivityPanel "wow" con cálculo en tiempo real, reporte web con filas expandibles de desglose de recursos, edición inline de proceso/cliente, tabla de recursos en PDF (página condicional), 548 tests. Ver case study: `simulador-web/methodology/CASE_STUDIES/inq-roi-sprint-2.md`. @@ -22,19 +24,22 @@ --- -## Sprint 4 — dirección estratégica +## Sprint 5 — dirección estratégica -Refactorización arquitectural completa. Sin features de producto nuevas. +UX polish + quick wins. Retorno al rebalanceo normal (60-70% web). Sin features de gran escala. -- **Auth:** Supabase + Google Workspace OAuth (`@inquality.com.py`). Abstracción `AuthService` para migración futura a Keycloak. -- **Base de datos:** reemplazo de IndexedDB/Dexie por PostgreSQL via Supabase. Datos existentes abandonados (eran solo pruebas). -- **Recursos globales:** catálogo compartido del equipo, sin `processId`. Rompe modelo actual — requiere mockup aprobado antes de Etapa 7. -- **Privacidad:** procesos con visibilidad `private | team`. Recursos, grupos y tags siempre compartidos. Simulaciones heredan del proceso. -- **Sin auditoría:** diferida a sprint dedicado. -- **Online-only:** aceptado. Offline en roadmap futuro. -- **Keycloak:** diferido. InQuality tiene VPS desa + prod. Keycloak será el auth server de la plataforma InQuality (multi-producto). +- **Fix crítico:** workspace loading — se queda cargando sin refresh +- **Fix + mejora:** zoom BPMN centering + controles zoom-in/zoom-out +- **Heatmap:** gradiente continuo (percentil default, toggle min-max) +- **Catálogo:** procesos por recurso + campo empresa en perfil +- **Reporte:** columna eficiencia de automatización en ComparisonTable +- **Deuda técnica:** E2E Dexie→Supabase + flakiness "Guardar" -Ver `sprints/sprint-4/BRIEF.md` para scope completo. +Ver `sprints/sprint-5/BRIEF.md` para scope completo. + +## Sprint 4 — dirección estratégica (cerrado) + +Refactorización arquitectural completa. Sin features de producto nuevas. Ver `sprints/sprint-4/BRIEF.md`. --- @@ -77,7 +82,7 @@ Historial de simulaciones y comparación de escenarios diferido a Sprint 3. - Stack tecnológico (React + Vite + TypeScript + stack definido en `simulador-web/CLAUDE.md`) - Modelo de escenarios: Modelo A (un BPMN, dos pares de campos por actividad) - ROI: cálculos nominales simples sin VPN ni Monte Carlo -- Heatmap: colores saturados `#10B981 / #F59E0B / #EF4444` (revisar Sprint 3-4) +- Heatmap: gradiente continuo 4 stops (#10b981→#f59e0b→#f97316→#ef4444), percentil default, toggle min-max (Sprint 5 Etapa 3) --- diff --git a/sprints/sprint-5/BRIEF.md b/sprints/sprint-5/BRIEF.md new file mode 100644 index 0000000..6d57cd6 --- /dev/null +++ b/sprints/sprint-5/BRIEF.md @@ -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 diff --git a/sprints/sprint-5/ETAPA_1_PROMPT.md b/sprints/sprint-5/ETAPA_1_PROMPT.md new file mode 100644 index 0000000..d445f6f --- /dev/null +++ b/sprints/sprint-5/ETAPA_1_PROMPT.md @@ -0,0 +1,192 @@ +# Sprint 5 — Etapa 1: Fix crítico workspace loading + +## Contexto + +InQ ROI — React 18 + Vite + TypeScript + TanStack Router + Zustand + shadcn/ui + Tailwind + Supabase. +Sprint 4 completado. La app tiene auth (Google OAuth + Supabase), PostgreSQL, RLS, catálogo de recursos global. + +**Bug en producción:** al navegar desde la biblioteca (`/`) a un proceso (`/workspace/:processId`), la pantalla del workspace queda cargando indefinidamente. Solo carga correctamente después de un refresh manual de la página. + +--- + +## Causa raíz — ya diagnosticada, no requiere investigación adicional + +El diagnóstico es preciso. No es necesario "investigar el flujo" — el problema está localizado. + +**`src/store/process-store.ts`** llama a `supabase.auth.getSession()` directamente como barrera de sesión antes del fetch. Esta llamada no tiene timeout: si Supabase tarda en responder (token refresh en curso, red lenta, lock interno), `getSession()` se bloquea indefinidamente → `loadProcess` nunca resuelve → `isLoading` queda `true` para siempre. + +**`src/store/library-store.ts`** hace lo mismo pero usa `getSessionSafe()` — una función en `src/lib/supabase.ts` que hace race entre `getSession()` y un timeout de 4 segundos, garantizando que siempre resuelve. Por eso la biblioteca carga y el workspace no. + +**La inconsistencia es sistémica:** el mismo patrón puede existir en otros stores. Esta etapa lo audita y corrige en todos. + +--- + +## Parte 1 — Fix en process-store.ts + +En `src/store/process-store.ts`, localizar la llamada a `supabase.auth.getSession()` (usada como barrera antes del query principal) y reemplazarla por `getSessionSafe()`: + +```typescript +// Antes: +await supabase.auth.getSession() + +// Después: +await getSessionSafe() +``` + +Agregar el import si no está: +```typescript +import { supabase, getSessionSafe } from '@/lib/supabase' +``` + +Verificar que `getSessionSafe` ya existe en `src/lib/supabase.ts`. Si por alguna razón no existe, crearla siguiendo el mismo patrón que se usa en `library-store.ts`. + +--- + +## Parte 2 — Auditar todos los stores + +Buscar en `src/store/` y `src/persistence/` cualquier archivo que llame a `supabase.auth.getSession()` directamente (sin pasar por `getSessionSafe`). Reportar qué archivos tienen este patrón. + +Para **cada archivo** que lo use: +- Si es una barrera de sesión antes de un query → reemplazar por `getSessionSafe()` +- Si es otro uso (ej: verificar si hay sesión activa para tomar una decisión, no como barrera) → evaluar caso por caso y documentar la decisión + +--- + +## Parte 3 — Estado de error en el workspace + +**Problema adicional:** actualmente, si `getSessionSafe()` agota su timeout, `loadProcess` lanza un error, pero el workspace no tiene un estado `error` propio. El resultado es que `isLoading` puede quedar `true` o la pantalla queda vacía sin mensaje al usuario. + +**Fix requerido:** en `process-store.ts` (y el hook/componente que lo consume), agregar manejo de estado de error: + +```typescript +// En el store, el estado debe incluir: +{ + isLoading: boolean + error: string | null // 🆕 + process: Process | null +} + +// En loadProcess, al catch: +set({ isLoading: false, error: 'No se pudo cargar el proceso. Revisá tu conexión.' }) +``` + +**En WorkspacePage.tsx** (o el componente raíz del workspace), manejar el estado `error`: + +```tsx +if (error) { + return ( +
{error}
+ +Algo salió mal al cargar el workspace.
+{this.state.error?.message}
+ +Algo salió mal al cargar el workspace.
+{this.state.error?.message}
+ ++ {error ?? 'No se pudo cargar el proceso. Revisá tu conexión.'} +
+