Refactor App component styles and remove highlights section
- Updated background gradient colors for a lighter theme. - Changed button styles to improve visibility. - Removed the highlights section to streamline the layout. - Adjusted text colors for better contrast and readability.
This commit is contained in:
+126
-88
@@ -1,109 +1,147 @@
|
||||
import { createClient, type User } from '@supabase/supabase-js'
|
||||
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,
|
||||
import.meta.env.VITE_SUPABASE_URL,
|
||||
import.meta.env.VITE_SUPABASE_ANON_KEY,
|
||||
)
|
||||
|
||||
export interface SupabaseAuthState {
|
||||
isAuthenticated: boolean
|
||||
user: User | null
|
||||
claims: UserClaims | null
|
||||
login: (email: string, password: string) => Promise<void>
|
||||
logout: () => Promise<void>
|
||||
isLoading: boolean
|
||||
isAuthenticated: boolean
|
||||
user: User | null
|
||||
claims: UserClaims | null
|
||||
login: (email: string, password: string) => Promise<void>
|
||||
logout: () => Promise<void>
|
||||
isLoading: boolean
|
||||
}
|
||||
|
||||
type Role =
|
||||
| 'lci'
|
||||
| 'vicerrectoria'
|
||||
| 'director_facultad' // 👈 NEW
|
||||
| 'secretario_academico'
|
||||
| 'jefe_carrera'
|
||||
| 'planeacion'
|
||||
|
||||
type UserClaims = {
|
||||
claims_admin: boolean,
|
||||
clave: string,
|
||||
nombre: string,
|
||||
apellidos: string,
|
||||
title: string,
|
||||
avatar: string | null,
|
||||
carrera_id?: string,
|
||||
facultad_id?: string,
|
||||
role: 'lci' | 'vicerrectoria' | 'secretario_academico' | 'jefe_carrera' | 'planeacion',
|
||||
claims_admin: boolean
|
||||
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<SupabaseAuthState | undefined>(
|
||||
undefined,
|
||||
)
|
||||
const SupabaseAuthContext = createContext<SupabaseAuthState | undefined>(undefined)
|
||||
|
||||
export function SupabaseAuthProvider({
|
||||
children,
|
||||
}: {
|
||||
children: React.ReactNode
|
||||
}) {
|
||||
const [user, setUser] = useState<User | null>(null)
|
||||
const [claims, setClaims] = useState<UserClaims | null>(null)
|
||||
const [isAuthenticated, setIsAuthenticated] = useState(false)
|
||||
const [isLoading, setIsLoading] = useState(true)
|
||||
export function SupabaseAuthProvider({ children }: { children: React.ReactNode }) {
|
||||
const [user, setUser] = useState<User | null>(null)
|
||||
const [claims, setClaims] = useState<UserClaims | null>(null)
|
||||
const [isAuthenticated, setIsAuthenticated] = useState(false)
|
||||
const [isLoading, setIsLoading] = useState(true)
|
||||
|
||||
useEffect(() => {
|
||||
// Get initial session
|
||||
supabase.auth.getSession().then(({ data: { session } }) => {
|
||||
setUser(session?.user ?? null)
|
||||
setClaims({
|
||||
...(session?.user?.app_metadata as Partial<UserClaims> ?? {}),
|
||||
...(session?.user?.user_metadata as Partial<UserClaims> ?? {}),
|
||||
} as UserClaims | null)
|
||||
setIsAuthenticated(!!session?.user)
|
||||
setIsLoading(false)
|
||||
})
|
||||
useEffect(() => {
|
||||
// Carga inicial
|
||||
supabase.auth.getSession().then(async ({ data: { session } }) => {
|
||||
const u = session?.user ?? null
|
||||
setUser(u)
|
||||
setIsAuthenticated(!!u)
|
||||
setClaims(await buildClaims(session))
|
||||
setIsLoading(false)
|
||||
})
|
||||
|
||||
// Listen for auth changes
|
||||
const {
|
||||
data: { subscription },
|
||||
} = supabase.auth.onAuthStateChange((_event, session) => {
|
||||
setUser(session?.user ?? null)
|
||||
setClaims({
|
||||
...(session?.user?.app_metadata as Partial<UserClaims> ?? {}),
|
||||
...(session?.user?.user_metadata as Partial<UserClaims> ?? {}),
|
||||
} as UserClaims | null)
|
||||
setIsAuthenticated(!!session?.user)
|
||||
setIsLoading(false)
|
||||
})
|
||||
// Suscripción a cambios de sesión
|
||||
const { data: { subscription } } = supabase.auth.onAuthStateChange(async (_event, session) => {
|
||||
const u = session?.user ?? null
|
||||
setUser(u)
|
||||
setIsAuthenticated(!!u)
|
||||
setClaims(await buildClaims(session))
|
||||
setIsLoading(false)
|
||||
})
|
||||
|
||||
return () => subscription.unsubscribe()
|
||||
}, [])
|
||||
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 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"
|
||||
}
|
||||
const logout = async () => {
|
||||
const { error } = await supabase.auth.signOut()
|
||||
if (error) throw error
|
||||
location.href = "/login"
|
||||
}
|
||||
|
||||
return (
|
||||
<SupabaseAuthContext.Provider
|
||||
value={{
|
||||
isAuthenticated,
|
||||
user,
|
||||
claims,
|
||||
login,
|
||||
logout,
|
||||
isLoading,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</SupabaseAuthContext.Provider>
|
||||
)
|
||||
return (
|
||||
<SupabaseAuthContext.Provider
|
||||
value={{ isAuthenticated, user, claims, login, logout, isLoading }}
|
||||
>
|
||||
{children}
|
||||
</SupabaseAuthContext.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
export function useSupabaseAuth() {
|
||||
const context = useContext(SupabaseAuthContext)
|
||||
if (context === undefined) {
|
||||
throw new Error('useSupabaseAuth must be used within SupabaseAuthProvider')
|
||||
}
|
||||
return context
|
||||
}
|
||||
const context = useContext(SupabaseAuthContext)
|
||||
if (context === undefined) {
|
||||
throw new Error('useSupabaseAuth must be used within SupabaseAuthProvider')
|
||||
}
|
||||
return context
|
||||
}
|
||||
|
||||
/* ===================== *
|
||||
* Helpers
|
||||
* ===================== */
|
||||
|
||||
// Unifica extracción de metadatos y resuelve facultad_color si hay facultad_id
|
||||
async function buildClaims(session: Session | null): Promise<UserClaims | null> {
|
||||
const u = session?.user
|
||||
if (!u) return null
|
||||
|
||||
const app = (u.app_metadata ?? {}) as Partial<UserClaims> & { role?: Role }
|
||||
const meta = (u.user_metadata ?? {}) as Partial<UserClaims>
|
||||
|
||||
// Mezcla segura: app_metadata > user_metadata (para campos de claims)
|
||||
const base: Partial<UserClaims> = {
|
||||
claims_admin: !!(app.claims_admin ?? (meta as any).claims_admin),
|
||||
role: (app.role as Role | undefined) ?? ('lci' as Role),
|
||||
facultad_id: app.facultad_id ?? meta.facultad_id ?? null,
|
||||
carrera_id: app.carrera_id ?? meta.carrera_id ?? null,
|
||||
clave: (meta.clave as string) ?? '',
|
||||
nombre: (meta.nombre as string) ?? '',
|
||||
apellidos: (meta.apellidos as string) ?? '',
|
||||
title: (meta.title as string) ?? '',
|
||||
avatar: (meta.avatar as string) ?? null,
|
||||
}
|
||||
|
||||
let facultad_color: string | null = null
|
||||
if (base.facultad_id) {
|
||||
// Lee color desde public.facultades
|
||||
const { data, error } = await supabase
|
||||
.from('facultades')
|
||||
.select('color')
|
||||
.eq('id', base.facultad_id)
|
||||
.maybeSingle()
|
||||
|
||||
if (!error && data) facultad_color = (data as any)?.color ?? null
|
||||
}
|
||||
|
||||
return {
|
||||
claims_admin: !!base.claims_admin,
|
||||
role: (base.role ?? 'lci') as Role,
|
||||
clave: base.clave ?? '',
|
||||
nombre: base.nombre ?? '',
|
||||
apellidos: base.apellidos ?? '',
|
||||
title: base.title ?? '',
|
||||
avatar: base.avatar ?? null,
|
||||
facultad_id: (base.facultad_id as string | null) ?? null,
|
||||
carrera_id: (base.carrera_id as string | null) ?? null,
|
||||
facultad_color, // 🎨
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user