fix(workspace): obtener orgName via JOIN en getById, eliminar fetch separado con race condition
Some checks failed
/ Deploy to Cloudflare Pages (push) Has been cancelled
Some checks failed
/ Deploy to Cloudflare Pages (push) Has been cancelled
This commit is contained in:
@@ -21,6 +21,7 @@ export interface Process {
|
|||||||
ownerId: string // 🆕 Sprint 4 — owner del proceso (auth.uid() al crear)
|
ownerId: string // 🆕 Sprint 4 — owner del proceso (auth.uid() al crear)
|
||||||
updatedBy: string // 🆕 Sprint 4 — UUID del usuario que hizo el último update
|
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)
|
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'
|
export type ActivityType = 'task' | 'subprocess'
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ import { useLibraryStore } from '@/store/library-store'
|
|||||||
import { useAuth } from '@/auth/AuthContext'
|
import { useAuth } from '@/auth/AuthContext'
|
||||||
import { AppHeader } from '@/components/AppHeader'
|
import { AppHeader } from '@/components/AppHeader'
|
||||||
import { ErrorBoundary } from '@/components/ErrorBoundary'
|
import { ErrorBoundary } from '@/components/ErrorBoundary'
|
||||||
import { supabase } from '@/lib/supabase'
|
|
||||||
|
|
||||||
type SelectedCategory = 'activity' | 'gateway' | null
|
type SelectedCategory = 'activity' | 'gateway' | null
|
||||||
|
|
||||||
@@ -68,18 +67,7 @@ export function WorkspacePage() {
|
|||||||
}
|
}
|
||||||
}, [user, processId, navigate])
|
}, [user, processId, navigate])
|
||||||
|
|
||||||
// Nombre de la organización dueña del proceso (para el back button contextual).
|
// orgName viene directamente del proceso cargado via JOIN en getById (sin fetch separado)
|
||||||
// undefined = todavía cargando, null = cargado sin resultado, string = nombre real.
|
|
||||||
const [orgName, setOrgName] = useState<string | null | undefined>(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])
|
|
||||||
|
|
||||||
// Validación XOR: todos los gateways exclusivos deben sumar 1.0
|
// Validación XOR: todos los gateways exclusivos deben sumar 1.0
|
||||||
const xorValidation = useMemo(() => {
|
const xorValidation = useMemo(() => {
|
||||||
@@ -198,10 +186,12 @@ export function WorkspacePage() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// client_viewer ya fue redirigido antes de este punto; solo verificamos client_editor aquí
|
// 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 isClientRole = user?.platformRole === 'client_editor'
|
||||||
const backLabel = isClientRole
|
const backLabel = isClientRole
|
||||||
? 'Procesos'
|
? 'Procesos'
|
||||||
: orgName ?? 'Biblioteca'
|
: currentProcess.orgName ?? 'Biblioteca'
|
||||||
|
|
||||||
function handleBack() {
|
function handleBack() {
|
||||||
const orgId = currentProcess?.orgId
|
const orgId = currentProcess?.orgId
|
||||||
|
|||||||
@@ -23,6 +23,8 @@ function toRow(process: Process, userId: string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function fromRow(row: Record<string, unknown>): Process {
|
function fromRow(row: Record<string, unknown>): Process {
|
||||||
|
// org puede estar presente si el query hizo JOIN con organizations (getById lo incluye)
|
||||||
|
const org = row.org as { name: string } | null
|
||||||
return {
|
return {
|
||||||
id: row.id as string,
|
id: row.id as string,
|
||||||
name: row.name as string,
|
name: row.name as string,
|
||||||
@@ -40,6 +42,7 @@ function fromRow(row: Record<string, unknown>): Process {
|
|||||||
ownerId: (row.owner_id as string) ?? '',
|
ownerId: (row.owner_id as string) ?? '',
|
||||||
updatedBy: (row.updated_by as string) ?? '',
|
updatedBy: (row.updated_by as string) ?? '',
|
||||||
orgId: (row.org_id as string | null) ?? null,
|
orgId: (row.org_id as string | null) ?? null,
|
||||||
|
orgName: org?.name ?? null,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -96,13 +99,14 @@ export const supabaseProcessRepo = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
async getById(id: string): Promise<Process | undefined> {
|
async getById(id: string): Promise<Process | undefined> {
|
||||||
|
// JOIN con organizations para obtener orgName en una sola query (evita fetch separado y race conditions)
|
||||||
const { data, error } = await supabase
|
const { data, error } = await supabase
|
||||||
.from('processes')
|
.from('processes')
|
||||||
.select('*')
|
.select('*, org:organizations!org_id(name)')
|
||||||
.eq('id', id)
|
.eq('id', id)
|
||||||
.single()
|
.single()
|
||||||
if (error) return undefined
|
if (error) return undefined
|
||||||
return fromRow(data)
|
return fromRow(data as Record<string, unknown>)
|
||||||
},
|
},
|
||||||
|
|
||||||
// JOIN con users para resolver owner/updater en una sola query (evita N+1).
|
// JOIN con users para resolver owner/updater en una sola query (evita N+1).
|
||||||
|
|||||||
Reference in New Issue
Block a user