Etapa 7B completa con Fix 4 agregado:
Some checks failed
/ Deploy to Cloudflare Pages (push) Has been cancelled

Fix 4 Parte A: .maybeSingle() ya estaba aplicado (lo hiciste vos/el linter). Encontré y arreglé un bug adicional: con data === null (sin simulaciones), el código llamaba fromRow(null) y explotaba al leer row.id. Ahora getLatestByProcess retorna undefined limpiamente cuando no hay filas.
Fix 4 Parte B: revisé ProcessCardWithSim en LibraryPage.tsx — ya tiene guard de cancelación y dependencias estables (process.id + acción de Zustand, que es una referencia fija). No encontré un loop real; lo más probable es que "~6 queries" correspondan a 6 procesos distintos disparando una query cada uno (correcto), posiblemente duplicado visualmente en dev por StrictMode (inocuo, no pasa en producción).
Fixes 1, 2, 3 ya estaban implementados de antes y siguen verdes. Falta tu validación visual — en particular confirmar que el network tab ya no muestra 406 en simulations.
This commit is contained in:
2026-06-19 00:33:46 -03:00
parent c738bc5489
commit 92597e108d
2 changed files with 52 additions and 8 deletions

View File

@@ -133,14 +133,56 @@ async signInWithGoogle(): Promise<void> {
---
## Fix 4 — simulations: 406 por `.single()` sin filas + re-fetch en loop
### Problema
En el network tab se observan múltiples requests con 406 Not Acceptable:
```
GET simulations?select=*&process_id=eq.{id}&order=executed_at.desc&limit=1 → 406
```
**Causa:** supabase-js v2 `.single()` envía `Accept: application/vnd.pgrst.object+json`, que exige exactamente 1 fila. Si el proceso no tiene simulaciones aún (0 filas), PostgREST responde 406. El mismo query aparece ~6 veces para los mismos `processId` — señal de un `useEffect` re-fetching por dependencia incorrecta.
### Solución
**Parte A — Fix del 406:** en `src/persistence/supabase/simulation-repo.ts`, en el método que busca la última simulación por proceso, cambiar `.single()` por `.maybeSingle()`:
```typescript
// Antes:
const { data, error } = await supabase
.from('simulations')
.select('*')
.eq('process_id', processId)
.order('executed_at', { ascending: false })
.limit(1)
.single() // ← lanza 406 cuando no hay filas
// Después:
const { data, error } = await supabase
.from('simulations')
.select('*')
.eq('process_id', processId)
.order('executed_at', { ascending: false })
.limit(1)
.maybeSingle() // ← retorna null sin error cuando no hay filas
```
El caller debe manejar `data === null` como "sin simulación" (no como error).
**Parte B — Fix del re-fetch en loop:** localizar el `useEffect` que dispara esta query (probablemente en `LibraryPage.tsx` o en un hook de simulación). Revisar que sus dependencias no incluyan un objeto o array que se re-crea en cada render (patrón clásico de loop infinito). Si el array de dependencias contiene `processes` (que es un array nuevo en cada render del store), estabilizarlo con `useMemo` o extraer solo los IDs que realmente cambian.
---
## Orden de ejecución
1. Fix 3 — `SupabaseAuthService.signInWithGoogle()` (1 línea, más fácil de verificar primero)
2. Fix 2función `formatCostPerHour` con precisión dinámica + aplicar en todos los contextos
3. Fix 1query param en ResourcesPanel + banner + botón en ResourceCatalogPage
4. `npm run build` — debe pasar sin errores TypeScript
5. `npm run test` — debe mantener 521+ tests verdes
6. Commit: `fix(sprint-4/etapa-7b): selector cuenta Google + redondeo costo/hora + contexto catálogo`
1. Fix 3 — `SupabaseAuthService.signInWithGoogle()` (1 línea)
2. Fix 4`.maybeSingle()` en simulation-repo + revisar re-fetch loop
3. Fix 2función `formatCostPerHour` con precisión dinámica + aplicar en todos los contextos
4. Fix 1 — query param en ResourcesPanel + banner + botón en ResourceCatalogPage
5. `npm run build` — debe pasar sin errores TypeScript
6. `npm run test` — debe mantener 521+ tests verdes
7. Commit: `fix(sprint-4/etapa-7b): selector cuenta Google + redondeo costo/hora + contexto catálogo + simulations maybeSingle`
---
@@ -153,6 +195,8 @@ async signInWithGoogle(): Promise<void> {
- [ ] Botón "← Usar en proceso" aparece en cada fila cuando hay contexto de proceso
- [ ] Click en "← Usar en proceso" navega a `/workspace/:processId`
- [ ] Navegar a `/recursos` directamente (sin `fromProcess`) no muestra banner ni botón extra
- [ ] Network tab sin 406 en requests de `simulations`
- [ ] Network tab: el query de simulaciones no se repite en loop para el mismo `processId`
- [ ] Build limpio sin errores TypeScript
- [ ] 521+ tests verdes
- [ ] **Validación visual del director** antes del OK formal