Los tres fixes descritos en ETAPA_3B_PROMPT.md se verificaron como ya
implementados en el commit de Etapa 3 (a65bec4):
- Fix 1 (repintado canvas): mode en deps de useEffect en HeatmapCanvas.tsx
existía desde el MVP — bug observado en estado intermedio de Etapa 3.
- Fix 2 (localStorage): useHeatmapMode() con lazy initializer + conectado
en ReportPage.tsx.
- Fix 3 (zoom position): bottom left: 12 en BpmnCanvas.tsx.
528 tests Vitest verdes. Build limpio.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
5.9 KiB
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():
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():
// Antes (estado local, no persiste):
const [mode, setMode] = useState<HeatmapMode>('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:
// ✅ Correcto — lee localStorage ANTES del primer render:
const [mode, setModeState] = useState<HeatmapMode>(() => {
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<HeatmapMode>('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.
// 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
- Fix 3 — posición controles zoom en
BpmnCanvas.tsx(1-2 líneas) - Fix 2 — conectar
useHeatmapMode()enReportPage.tsx(reemplazar useState local) - Fix 1 — agregar
modecomo dependencia en el useEffect que llamaapplyHeatmapToViewer() npm run build— 0 errores TypeScriptnpm run lint— 0 warnings nuevosnpm run test— 528+ tests Vitest verdes- 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