Sprint 5 — Etapa 2: resumen
Some checks failed
/ Deploy to Cloudflare Pages (push) Has been cancelled
Some checks failed
/ Deploy to Cloudflare Pages (push) Has been cancelled
Fix 0 (skeleton carry-over): el guard de Etapa 1 era isLoading && !currentProcess — fallaba al navegar directo entre dos workspaces distintos, porque el proceso anterior seguía en el store mientras el nuevo cargaba. Cambié a if (isLoading) return <WorkspaceSkeleton /> sin condición adicional, exactamente como pedía el brief.
Fix 1 (fit-viewport): una línea — el botón llamaba canvas.zoom('fit-viewport') sin 'auto' (la carga inicial sí lo tenía). Agregado.
Fix 2 (controles de zoom): agregué ZoomIn/ZoomOut de Lucide (no Tabler — ya está en el stack del proyecto, son visualmente equivalentes) en la esquina inferior derecha, apilados verticalmente, mismo estilo que el botón de encuadre (32×32px, fondo blanco semitransparente). Factor 1.2x, límites 0.1x–4x tal como especificaba el brief.
Verificación:
Build limpio
Lint: 2 warnings any nuevos en BpmnCanvas.tsx, consistentes con las 9 que ya existían en ese mismo archivo (el propio snippet del brief los prescribe así; bpmn-js tipa débilmente viewer.get()) — no es una categoría nueva de problema, lo marco para que quede explícito
Vitest: 521/521 verdes
E2E smoke: 9/9 verdes
Falta tu validación visual — en particular probar con Slow 3G que aparece el skeleton (no el spinner), y confirmar que el encuadre centra el diagrama y los botones de zoom no tapan nada.
This commit is contained in:
156
sprints/sprint-5/ETAPA_2_PROMPT.md
Normal file
156
sprints/sprint-5/ETAPA_2_PROMPT.md
Normal file
@@ -0,0 +1,156 @@
|
||||
# Sprint 5 — Etapa 2: Fix zoom BPMN + skeleton carry-over
|
||||
|
||||
## Contexto
|
||||
|
||||
InQ ROI — React 18 + Vite + TypeScript + TanStack Router + Zustand + shadcn/ui + Tailwind + Supabase.
|
||||
Etapa 1 completada con OK formal. Workspace carga correctamente. Dos ítems pendientes de esta etapa:
|
||||
|
||||
1. **Carry-over de Etapa 1:** `WorkspaceSkeleton.tsx` fue creado y está bien implementado, pero no aparece en la práctica — el spinner del router intercepta antes. Corregir la condición de render en `WorkspacePage.tsx`.
|
||||
2. **Fix + mejora del canvas BPMN:** botón fit-viewport con bug (clip al borde superior) y ausencia de controles zoom-in/zoom-out.
|
||||
|
||||
---
|
||||
|
||||
## Fix 0 — Skeleton carry-over (Etapa 1)
|
||||
|
||||
### Diagnóstico
|
||||
|
||||
`WorkspaceSkeleton.tsx` existe en `src/features/workspace/` y está bien implementado (layout completo con `animate-pulse`). El problema está en `WorkspacePage.tsx`: el skeleton no se renderiza cuando `isLoading === true` en el store, porque TanStack Router muestra su propio indicador de carga antes de que `WorkspacePage` monte y evalúe su estado interno.
|
||||
|
||||
### Fix
|
||||
|
||||
Abrir `WorkspacePage.tsx` (o `WorkspacePageWithBoundary` si así quedó tras Etapa 1) y localizar la condición de loading. Debe quedar así:
|
||||
|
||||
```tsx
|
||||
// Al inicio del componente, después de leer el store:
|
||||
const { isLoading, error, process } = useProcessStore()
|
||||
|
||||
if (isLoading) return <WorkspaceSkeleton />
|
||||
if (error) return <WorkspaceError error={error} onRetry={...} onBack={...} />
|
||||
if (!process) return <WorkspaceSkeleton /> // guard adicional para estado transitorio
|
||||
```
|
||||
|
||||
El objetivo es que el skeleton aparezca mientras `isLoading === true` **desde el componente**, no depender del loader de TanStack Router para mostrar feedback visual al usuario.
|
||||
|
||||
Si el router-level loader también muestra algo, no hay conflicto — el router puede tener su indicador mínimo (barra de progreso en el top si la hay) y el componente muestra el skeleton completo una vez montado. No eliminar el router loader si existe, solo asegurarse de que el componente también maneje su propio estado de carga con el skeleton.
|
||||
|
||||
**Verificación:** con DevTools → Network → Slow 3G, navegar desde biblioteca a proceso. Debe aparecer el skeleton (bloques grises animados preservando el layout) durante la carga, no una rueda giratoria sobre fondo en blanco.
|
||||
|
||||
---
|
||||
|
||||
## Fix 1 — Fit-viewport: botón sin argumento 'auto'
|
||||
|
||||
### Diagnóstico
|
||||
|
||||
En `BpmnCanvas.tsx`, el botón de encuadre llama:
|
||||
|
||||
```ts
|
||||
canvas.zoom('fit-viewport') // ← sin segundo argumento
|
||||
```
|
||||
|
||||
El load inicial correcto usa:
|
||||
|
||||
```ts
|
||||
canvas.zoom('fit-viewport', 'auto') // ← con 'auto', centra el diagrama
|
||||
```
|
||||
|
||||
Sin `'auto'`, bpmn-js hace fit pero sin centrar — el diagrama queda pegado al borde superior izquierdo del viewport. Con `'auto'`, centra el diagrama en el viewport con el zoom justo para que entre.
|
||||
|
||||
### Fix
|
||||
|
||||
En el handler del botón de encuadre (botón con ícono `Maximize2` en `BpmnCanvas.tsx`), agregar el argumento `'auto'`:
|
||||
|
||||
```ts
|
||||
// Antes:
|
||||
canvas.zoom('fit-viewport')
|
||||
|
||||
// Después:
|
||||
canvas.zoom('fit-viewport', 'auto')
|
||||
```
|
||||
|
||||
Una línea. No tocar el resto del componente.
|
||||
|
||||
---
|
||||
|
||||
## Fix 2 — Controles de zoom-in y zoom-out
|
||||
|
||||
### Contexto del layout
|
||||
|
||||
`BpmnCanvas.tsx` tiene:
|
||||
- Wrapper externo: `position: relative, width: 100%, height: 100%`
|
||||
- `containerRef` (donde monta bpmn-js): `width: 100%, height: 100%`
|
||||
- Botón fit-viewport existente: `position: absolute, top: 12, right: 12`
|
||||
|
||||
Los botones nuevos se posicionan sobre el canvas usando `position: absolute`.
|
||||
|
||||
### Implementación
|
||||
|
||||
Agregar dos botones: zoom-in (+) y zoom-out (−). Junto al botón fit existente, formando un grupo de 3 controles.
|
||||
|
||||
**Posición:** esquina inferior derecha del canvas (no esquina superior — para no interferir con el botón fit existente que ya está arriba). Usar `bottom: 12, right: 12`.
|
||||
|
||||
**Lógica:**
|
||||
```ts
|
||||
const handleZoomIn = () => {
|
||||
if (!viewerRef.current) return
|
||||
const canvas = viewerRef.current.get('canvas') as any
|
||||
const currentZoom = canvas.zoom()
|
||||
canvas.zoom(Math.min(currentZoom * 1.2, 4)) // máximo zoom: 4x
|
||||
}
|
||||
|
||||
const handleZoomOut = () => {
|
||||
if (!viewerRef.current) return
|
||||
const canvas = viewerRef.current.get('canvas') as any
|
||||
const currentZoom = canvas.zoom()
|
||||
canvas.zoom(Math.max(currentZoom / 1.2, 0.1)) // mínimo zoom: 0.1x
|
||||
}
|
||||
```
|
||||
|
||||
Los límites `min: 0.1` y `max: 4` previenen que el usuario llegue a un estado de zoom irrecuperable.
|
||||
|
||||
**Estilo de los botones** — consistente con el botón fit existente:
|
||||
- Tamaño: 28×28px o 32×32px (el que use el botón fit actual)
|
||||
- Fondo: blanco semitransparente con borde sutil
|
||||
- Ícono Tabler: `ti-zoom-in` para +, `ti-zoom-out` para −
|
||||
- Separación entre botones: 4px (grupo compacto)
|
||||
- Disposición: verticalmente apilados (+ arriba, − abajo) o en fila horizontal — lo que quede más compacto sin solapar el diagrama
|
||||
|
||||
**No reemplazar el zoom por scroll/pinch** — esos eventos de bpmn-js siguen funcionando igual. Estos botones son controles adicionales para usuarios que no tienen scroll o prefieren click explícito.
|
||||
|
||||
**Extraer en subcomponente si mejora la legibilidad** — si el bloque de JSX del canvas empieza a crecer, considerar un `<BpmnZoomControls onZoomIn={...} onZoomOut={...} onFit={...} />` separado. Solo si la extracción es natural; no forzar si el componente ya es compacto.
|
||||
|
||||
---
|
||||
|
||||
## Qué NO entra en esta etapa
|
||||
|
||||
- Cambios en la lógica de simulación o cálculos
|
||||
- Cambios en paneles laterales (ActivityPanel, ResourcesPanel)
|
||||
- Cambios en el reporte o PDF
|
||||
- Nuevas features de producto
|
||||
- Cambios en auth o Supabase
|
||||
|
||||
---
|
||||
|
||||
## Orden de ejecución
|
||||
|
||||
1. Fix 0 — `WorkspacePage.tsx`: renderizar `WorkspaceSkeleton` cuando `isLoading === true`
|
||||
2. Fix 1 — `BpmnCanvas.tsx`: agregar `'auto'` al botón fit-viewport
|
||||
3. Fix 2 — `BpmnCanvas.tsx`: agregar botones zoom-in y zoom-out en esquina inferior derecha
|
||||
4. `npm run build` — 0 errores TypeScript
|
||||
5. `npm run lint` — 0 warnings nuevos
|
||||
6. `npm run test` — mantener 521+ tests Vitest verdes
|
||||
7. `npx playwright test tests/e2e/sprint-4-smoke.spec.ts` — 9/9 verdes
|
||||
8. Commit: `fix(sprint-5/etapa-2): skeleton workspace + fit-viewport center + zoom controls`
|
||||
|
||||
---
|
||||
|
||||
## Criterios de validación
|
||||
|
||||
- [ ] Con Slow 3G: navegar desde biblioteca a proceso muestra skeleton (no spinner genérico) durante la carga
|
||||
- [ ] Botón fit-viewport centra el diagrama con padding visible en todos los bordes (no clip al borde superior)
|
||||
- [ ] Botones zoom-in y zoom-out visibles en el canvas
|
||||
- [ ] Zoom-in hace la imagen más grande (factor ~1.2x por click)
|
||||
- [ ] Zoom-out hace la imagen más pequeña (factor ~1.2x por click)
|
||||
- [ ] Zoom por scroll/pinch sigue funcionando sin interferencia
|
||||
- [ ] Los controles no tapan el diagrama ni los paneles laterales
|
||||
- [ ] Build limpio, lint sin warnings nuevos, 521+ tests Vitest verdes
|
||||
- [ ] **Validación visual del director** antes del OK formal
|
||||
Reference in New Issue
Block a user