diff --git a/OBSERVATIONS.md b/OBSERVATIONS.md index b9e71e7..772e296 100644 --- a/OBSERVATIONS.md +++ b/OBSERVATIONS.md @@ -37,6 +37,23 @@ --- +### [RESOLVED] Etapa 3 — Heatmap gradiente 4 stops + persistencia de modo + zoom position + +**Estado:** pendiente OK formal del director. +**Fixes Etapa 3:** +- `colors.ts`: gradiente continuo verde→amarillo→naranja→rojo (interpolación genérica N-stops), modos `'percentile'`/`'minmax'` reemplazan `'relative'`/`'absolute'` +- `useHeatmapMode.ts` (hook nuevo): persiste preferencia en `localStorage` con lazy initializer en `useState` +- `ReportPage.tsx`: reemplaza `useState('relative')` por `useHeatmapMode()` +- `BpmnCanvas.tsx`: zoom-in/zoom-out movidos de `right:12` a `left:12` (evita solapar logo bpmn.io) +- PDF: leyenda de 4 stops con interpolación. Tests: 528 Vitest verdes. E2E recalibrados con distancia euclidiana. + +**Fixes Etapa 3B verificados (incluidos en Etapa 3):** +- Fix 1 (repintado de canvas al cambiar toggle): `mode` ya estaba como dependencia del `useEffect` en `HeatmapCanvas.tsx` desde el MVP. El bug fue observado durante implementación intermedia; resuelto con los renombres de Etapa 3. +- Fix 2 (persistencia localStorage): `useHeatmapMode()` con lazy initializer correcto; `ReportPage` usa el hook. +- Fix 3 (zoom position): `left: 12` en `BpmnCanvas.tsx`. + +--- + ## Hallazgos diferidos (promocionar al cerrar sprint) *(vacío)* diff --git a/sprints/sprint-5/ETAPA_3B_PROMPT.md b/sprints/sprint-5/ETAPA_3B_PROMPT.md new file mode 100644 index 0000000..dd46df5 --- /dev/null +++ b/sprints/sprint-5/ETAPA_3B_PROMPT.md @@ -0,0 +1,138 @@ +# Sprint 5 — Etapa 3B: Tres fixes post-validación visual + +## Contexto + +InQ ROI — React 18 + Vite + TypeScript + TanStack Router + Zustand + shadcn/ui + Tailwind + Supabase. +Etapa 3 completada técnicamente. Durante la validación visual del director se detectaron 3 bugs que bloquean el OK formal. Esta etapa los corrige antes de avanzar a Etapa 4. + +--- + +## Fix 1 — Toggle no re-pinta el heatmap del canvas + +### Síntoma +Al cambiar entre "Costo relativo (%)" y "Costo absoluto" en el reporte, el toggle cambia visualmente pero los colores del canvas BPMN y de la tabla de actividades no se actualizan. + +### Causa probable +`applyHeatmapToViewer()` en `HeatmapCanvas.tsx` se llama una sola vez después de la simulación (en un `useEffect` con dependencia en los datos de simulación). Cuando el `mode` cambia, ese efecto no se re-ejecuta — el canvas ya está pintado y no se re-pinta. + +### Fix + +**Parte A — Canvas BPMN:** +En el componente que contiene `HeatmapCanvas` (probablemente `ReportPage.tsx` o el propio `HeatmapCanvas.tsx`), agregar `mode` como dependencia del `useEffect` que llama a `applyHeatmapToViewer()`: + +```tsx +useEffect(() => { + if (!simulationData || !viewerReady) return + applyHeatmapToViewer(viewer, simulationData.activities, mode) +}, [simulationData, viewerReady, mode]) // ← agregar mode +``` + +Si `applyHeatmapToViewer` recibe el mode como argumento, verificar que se lo está pasando correctamente. Si no lo recibe aún, agregarlo como parámetro. + +**Parte B — Tabla de actividades:** +Verificar que `ActivitiesTable.tsx` recibe el `mode` actualizado como prop. Si el `mode` viene del mismo estado del toggle, la tabla debería re-renderizarse automáticamente. Si hay una desconexión (el toggle actualiza un estado local en un componente y la tabla lee de otro), unificarlos para que ambos lean del mismo `useHeatmapMode()` hook. + +**El resultado esperado:** cambiar el toggle debe re-pintar el canvas BPMN y actualizar los colores de la tabla en tiempo real, sin necesidad de re-simular. + +--- + +## Fix 2 — Persistencia del modo no funciona (Cmd+R resetea a default) + +### Síntoma +Al seleccionar "Costo absoluto" y recargar la página (Cmd+R), el toggle vuelve a "Costo relativo (%)". La preferencia no persiste en localStorage. + +### Causa probable +`ReportPage.tsx` probablemente todavía usa su propio `useState('relative')` o `useState('percentile')` en lugar de consumir `useHeatmapMode()`. El hook fue creado pero no conectado al componente. + +### Fix + +**Paso 1:** Buscar en `ReportPage.tsx` (y cualquier componente que renderice el toggle) si usa `useState` para el modo del heatmap. Si lo hace, reemplazarlo por `useHeatmapMode()`: + +```tsx +// Antes (estado local, no persiste): +const [mode, setMode] = useState('percentile') + +// Después (hook con localStorage): +const [mode, setMode] = useHeatmapMode() +``` + +**Paso 2:** Verificar que `useHeatmapMode()` lee correctamente de localStorage al inicializar. La implementación debe leer el valor almacenado en el `useState` initializer (función lazy), no en un `useEffect`: + +```tsx +// ✅ Correcto — lee localStorage ANTES del primer render: +const [mode, setModeState] = useState(() => { + try { + const stored = localStorage.getItem('inq-roi:heatmap-mode') + return (stored === 'percentile' || stored === 'minmax') ? stored : 'percentile' + } catch { + return 'percentile' + } +}) + +// ❌ Incorrecto — el useEffect corre DESPUÉS del primer render, hay flash: +const [mode, setModeState] = useState('percentile') +useEffect(() => { + const stored = localStorage.getItem('inq-roi:heatmap-mode') + if (stored) setModeState(stored as HeatmapMode) +}, []) +``` + +**Verificación:** tras el fix, cambiar a "Costo absoluto" → Cmd+R → el toggle debe aparecer en "Costo absoluto" desde el primer render, sin flash. + +--- + +## Fix 3 — Controles de zoom siguen solapando logo bpmn.io + +### Síntoma +Los botones de zoom-in/zoom-out en el canvas BPMN siguen en la esquina inferior derecha, solapando el watermark `BPMN.iO`. + +### Causa probable +El Fix 0 de Etapa 3 no se aplicó correctamente, o se cambió la posición del botón de encuadre (fit) en lugar de los controles de zoom-in/zoom-out. + +### Fix + +Abrir `BpmnCanvas.tsx`. Localizar el grupo de botones de zoom-in y zoom-out. Debe estar posicionado con `position: 'absolute'`. + +**Si está en `right: 12`:** cambiar a `left: 12`. El botón de encuadre (fit-viewport, ícono `Maximize2` o `Focus`) en la esquina superior derecha **no se mueve** — ese no solapa nada. + +```tsx +// Grupo de controles zoom-in / zoom-out: +// Antes: +style={{ position: 'absolute', bottom: 12, right: 12 }} + +// Después: +style={{ position: 'absolute', bottom: 12, left: 12 }} +``` + +**Verificación:** el logo `BPMN.iO` debe ser completamente visible en la esquina inferior derecha. Los controles de zoom deben estar en la esquina inferior izquierda sin tapar ningún elemento del canvas. + +--- + +## Qué NO entra en esta etapa + +- Cambios en la lógica de simulación +- Cambios en el esquema de Supabase +- Nuevas features fuera del heatmap y controles de zoom + +--- + +## Orden de ejecución + +1. Fix 3 — posición controles zoom en `BpmnCanvas.tsx` (1-2 líneas) +2. Fix 2 — conectar `useHeatmapMode()` en `ReportPage.tsx` (reemplazar useState local) +3. Fix 1 — agregar `mode` como dependencia en el useEffect que llama `applyHeatmapToViewer()` +4. `npm run build` — 0 errores TypeScript +5. `npm run lint` — 0 warnings nuevos +6. `npm run test` — 528+ tests Vitest verdes +7. Commit: `fix(sprint-5/etapa-3b): heatmap toggle repaint + localStorage + zoom position` + +--- + +## Criterios de validación + +- [ ] Cambiar el toggle re-pinta el canvas BPMN en tiempo real (sin re-simular) +- [ ] Cambiar el toggle actualiza los colores de la tabla de actividades en tiempo real +- [ ] Seleccionar "Costo absoluto" → Cmd+R → el toggle sigue en "Costo absoluto" +- [ ] Controles zoom-in/zoom-out en esquina inferior izquierda sin solapar bpmn.io +- [ ] 528+ tests Vitest verdes +- [ ] **Validación visual del director** antes del OK formal