From df5fb5569692402d9eb3fdc4f82d29ab87b452de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcos=20Ben=C3=ADtez?= Date: Tue, 7 Jul 2026 15:26:52 -0300 Subject: [PATCH] fix(workspace): obtener orgName via JOIN en getById, eliminar fetch separado con race condition --- src/domain/types.ts | 1 + src/features/workspace/WorkspacePage.tsx | 18 ++++-------------- src/persistence/supabase/process-repo.ts | 8 ++++++-- 3 files changed, 11 insertions(+), 16 deletions(-) diff --git a/src/domain/types.ts b/src/domain/types.ts index dfd6c40..497c22c 100644 --- a/src/domain/types.ts +++ b/src/domain/types.ts @@ -21,6 +21,7 @@ export interface Process { ownerId: string // 🆕 Sprint 4 — owner del proceso (auth.uid() al crear) updatedBy: string // 🆕 Sprint 4 — UUID del usuario que hizo el último update orgId: string | null // 🆕 Sprint 7 — organización dueña del proceso (null = proceso interno InQ) + orgName?: string | null // 🆕 Sprint 7 — nombre de la org (poblado por getById via JOIN, no se persiste) } export type ActivityType = 'task' | 'subprocess' diff --git a/src/features/workspace/WorkspacePage.tsx b/src/features/workspace/WorkspacePage.tsx index 62d3f3c..7c5d7b3 100644 --- a/src/features/workspace/WorkspacePage.tsx +++ b/src/features/workspace/WorkspacePage.tsx @@ -22,7 +22,6 @@ import { useLibraryStore } from '@/store/library-store' import { useAuth } from '@/auth/AuthContext' import { AppHeader } from '@/components/AppHeader' import { ErrorBoundary } from '@/components/ErrorBoundary' -import { supabase } from '@/lib/supabase' type SelectedCategory = 'activity' | 'gateway' | null @@ -68,18 +67,7 @@ export function WorkspacePage() { } }, [user, processId, navigate]) - // Nombre de la organización dueña del proceso (para el back button contextual). - // undefined = todavía cargando, null = cargado sin resultado, string = nombre real. - const [orgName, setOrgName] = useState(undefined) - useEffect(() => { - if (!currentProcess?.orgId) { setOrgName(null); return } - supabase - .from('organizations') - .select('name') - .eq('id', currentProcess.orgId) - .maybeSingle() - .then(({ data }) => setOrgName(data?.name ?? null)) - }, [currentProcess?.orgId]) + // orgName viene directamente del proceso cargado via JOIN en getById (sin fetch separado) // Validación XOR: todos los gateways exclusivos deben sumar 1.0 const xorValidation = useMemo(() => { @@ -198,10 +186,12 @@ export function WorkspacePage() { } // client_viewer ya fue redirigido antes de este punto; solo verificamos client_editor aquí + // eslint-disable-next-line no-console + console.log('[WorkspacePage] currentProcess:', { id: currentProcess.id, orgId: currentProcess.orgId }) const isClientRole = user?.platformRole === 'client_editor' const backLabel = isClientRole ? 'Procesos' - : orgName ?? 'Biblioteca' + : currentProcess.orgName ?? 'Biblioteca' function handleBack() { const orgId = currentProcess?.orgId diff --git a/src/persistence/supabase/process-repo.ts b/src/persistence/supabase/process-repo.ts index 0651eb7..739aa3f 100644 --- a/src/persistence/supabase/process-repo.ts +++ b/src/persistence/supabase/process-repo.ts @@ -23,6 +23,8 @@ function toRow(process: Process, userId: string) { } function fromRow(row: Record): Process { + // org puede estar presente si el query hizo JOIN con organizations (getById lo incluye) + const org = row.org as { name: string } | null return { id: row.id as string, name: row.name as string, @@ -40,6 +42,7 @@ function fromRow(row: Record): Process { ownerId: (row.owner_id as string) ?? '', updatedBy: (row.updated_by as string) ?? '', orgId: (row.org_id as string | null) ?? null, + orgName: org?.name ?? null, } } @@ -96,13 +99,14 @@ export const supabaseProcessRepo = { }, async getById(id: string): Promise { + // JOIN con organizations para obtener orgName en una sola query (evita fetch separado y race conditions) const { data, error } = await supabase .from('processes') - .select('*') + .select('*, org:organizations!org_id(name)') .eq('id', id) .single() if (error) return undefined - return fromRow(data) + return fromRow(data as Record) }, // JOIN con users para resolver owner/updater en una sola query (evita N+1).