import { createClient, type User, type Session } from '@supabase/supabase-js' import { createContext, useContext, useEffect, useState } from 'react' export const supabase = createClient( import.meta.env.VITE_SUPABASE_URL, import.meta.env.VITE_SUPABASE_ANON_KEY, ) export interface SupabaseAuthState { isAuthenticated: boolean user: User | null claims: UserClaims | null roles: RolCatalogo[] | null login: (email: string, password: string) => Promise logout: () => Promise isLoading: boolean } export interface RolCatalogo { id: string nombre: string icono: string nombre_clase: string label: string } export type Role = string; export type UserClaims = { id: string | null clave?: string nombre: string apellidos: string title?: string avatar?: string | null carrera_id?: string | null facultad_id?: string | null facultad_color?: string | null // 馃帹 NEW role: Role } const SupabaseAuthContext = createContext(undefined) export function SupabaseAuthProvider({ children }: { children: React.ReactNode }) { const [user, setUser] = useState(null) const [claims, setClaims] = useState(null) const [roles, setRoles] = useState(null) const [isAuthenticated, setIsAuthenticated] = useState(false) const [isLoading, setIsLoading] = useState(true) useEffect(() => { // Funci贸n para manejar la sesi贸n const handleSession = async (session: Session | null) => { const u = session?.user ?? null setUser(u) setIsAuthenticated(!!u) setClaims(await buildClaims(session)) setIsLoading(false) } // Carga inicial supabase.auth.getSession().then(({ data: { session } }) => { handleSession(session) }) // Carga roles cat谩logo fetchRoles().then(fetchedRoles => { setRoles(fetchedRoles); }); // Suscripci贸n a cambios de sesi贸n const { data: { subscription } } = supabase.auth.onAuthStateChange((_event, session) => { handleSession(session) }) return () => subscription.unsubscribe() }, []) const login = async (email: string, password: string) => { const { error } = await supabase.auth.signInWithPassword({ email, password }) if (error?.code === 'invalid_credentials') throw new Error('Credenciales inv谩lidas') else if (error) throw error } const logout = async () => { const { error } = await supabase.auth.signOut() if (error) throw error location.href = "/login" } return ( {children} ) } export function useSupabaseAuth() { const context = useContext(SupabaseAuthContext) if (context === undefined) { throw new Error('useSupabaseAuth must be used within SupabaseAuthProvider') } return context } /* ===================== * * Helpers * ===================== */ // Obtiene los claims del usuario desde la base de datos a partir de una funci贸n en la BDD async function buildClaims(session: Session | null): Promise { // Validar sesi贸n if (!session || !session.user) { console.warn('No session or user found'); return null; } const u = session.user; try{ const result = await supabase.rpc('obtener_claims_usuario', { p_user_id: u.id, }); const data: UserClaims[] | null = result.data; const error = result.error; if (error) { console.error('Error al obtener la informaci贸n:', error); throw new Error('Error al obtener la informaci贸n del usuario'); } console.log(data); if (!data || data.length === 0) { console.warn('No se encontr贸 informaci贸n para el usuario'); return null; } return { ...data[0], id: null }; } catch (e) { console.error('Error inesperado:', e); return null; } } async function fetchRoles(): Promise { const { data, error } = await supabase .from("roles_catalogo") .select("id, nombre, icono, nombre_clase, label"); if (error) { console.error("Error al obtener los roles:", error.message); return []; } return data || []; }