feat(sprint-5/etapa-4): procesos por recurso + campo empresa en perfil

Feature A — Ver procesos asociados a un recurso:
- resource-repo.ts: getProcessCountsPerResource() (una query, N+1 evitado)
- resource-repo.ts: getProcessesForResource() (on-demand al expandir)
- ResourceCatalogPage: carga conteos en paralelo, columna "Procesos" con
  badge naranja clicable, fila expandida con links a /workspace/:id

Feature B — Campo empresa en perfil:
- 013_add_company_to_users.sql: ALTER TABLE users ADD COLUMN IF NOT EXISTS company text
- AuthUser: agrega company? opcional
- SupabaseAuthService: lee company y name de tabla users en onAuthStateChange
- AuthContext: agrega updateUser() para reflejar cambios sin recargar
- ProfileSheet.tsx: Sheet con nombre/empresa editables, email read-only
- AppHeader: muestra empresa en dropdown + item "Editar perfil"

528 tests Vitest verdes. Build limpio.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-19 19:56:13 -03:00
parent e8a4a1b3e0
commit 6d14e64f33
8 changed files with 354 additions and 117 deletions

View File

@@ -7,6 +7,7 @@ interface AuthContextValue {
loading: boolean
signIn: () => Promise<void>
signOut: () => Promise<void>
updateUser: (updates: Partial<Pick<AuthUser, 'name' | 'company'>>) => void
}
const AuthContext = createContext<AuthContextValue | null>(null)
@@ -78,8 +79,12 @@ export function AuthProvider({ children }: { children: ReactNode }) {
setUser(null)
}, [])
const updateUser = useCallback((updates: Partial<Pick<AuthUser, 'name' | 'company'>>) => {
setUser((prev) => prev ? { ...prev, ...updates } : prev)
}, [])
return (
<AuthContext.Provider value={{ user, loading, signIn, signOut }}>
<AuthContext.Provider value={{ user, loading, signIn, signOut, updateUser }}>
{children}
</AuthContext.Provider>
)

View File

@@ -60,8 +60,19 @@ export class SupabaseAuthService implements AuthService {
if (upsertError) console.error('Error al crear usuario en DB:', upsertError)
}
// buildAuthUser es sincrónico — sin DB query, usa metadatos de la sesión
const user = this.buildAuthUser(session.user)
// Leer company y name editado desde la tabla users (pueden diferir del user_metadata de Google)
const { data: userRow } = await supabase
.from('users')
.select('company, name')
.eq('id', session.user.id)
.maybeSingle()
const base = this.buildAuthUser(session.user)
const user: AuthUser = {
...base,
company: (userRow?.company as string | null) ?? undefined,
name: (userRow?.name as string | null) ?? base.name,
}
callback(user)
} catch (err) {
console.error('Error en onAuthStateChange:', err)

View File

@@ -4,6 +4,7 @@ export interface AuthUser {
name: string
avatarUrl?: string
platformRole: 'platform_admin' | 'member' | 'guest'
company?: string
}
export interface AuthService {