Initial commit

This commit is contained in:
Jose Selesan
2026-04-08 22:53:11 -03:00
commit 9ae270609d
179 changed files with 28096 additions and 0 deletions

View File

@@ -0,0 +1,166 @@
import {
createContext,
useContext,
useEffect,
useMemo,
useState,
type PropsWithChildren,
} from 'react'
import type { Session, User } from '@supabase/supabase-js'
import { setApiAccessToken } from '@/lib/api-client'
import { supabase } from '@/lib/supabase'
type SignInParams = {
email: string
password: string
}
type SignUpParams = {
email: string
password: string
fullName: string
}
export type AuthContextValue = {
user: User | null
session: Session | null
loading: boolean
isAuthenticated: boolean
displayName: string
initials: string
avatarUrl: string | null
signInWithPassword: (params: SignInParams) => Promise<void>
signUp: (params: SignUpParams) => Promise<{ requiresEmailConfirmation: boolean }>
signOut: () => Promise<void>
}
const AuthContext = createContext<AuthContextValue | null>(null)
function getDisplayName(user: User | null): string {
if (!user) return 'Usuario'
const fromMetadata =
user.user_metadata?.name ??
user.user_metadata?.full_name ??
user.user_metadata?.user_name
if (typeof fromMetadata === 'string' && fromMetadata.trim().length > 0) {
return fromMetadata.trim()
}
return user.email ?? 'Usuario'
}
function getInitials(name: string): string {
const words = name.trim().split(/\s+/).slice(0, 2)
const initials = words.map((word) => word[0]?.toUpperCase() ?? '').join('')
return initials || 'U'
}
function getAvatarUrl(user: User | null): string | null {
if (!user) return null
const value = user.user_metadata?.avatar_url ?? user.user_metadata?.picture
return typeof value === 'string' && value.length > 0 ? value : null
}
export function AuthProvider({ children }: PropsWithChildren) {
const [session, setSession] = useState<Session | null>(null)
const [user, setUser] = useState<User | null>(null)
const [loading, setLoading] = useState(true)
useEffect(() => {
let mounted = true
const init = async () => {
const { data } = await supabase.auth.getSession()
if (!mounted) return
setSession(data.session)
setUser(data.session?.user ?? null)
setApiAccessToken(data.session?.access_token ?? null)
setLoading(false)
}
void init()
const {
data: { subscription },
} = supabase.auth.onAuthStateChange((_event, nextSession) => {
setSession(nextSession)
setUser(nextSession?.user ?? null)
setApiAccessToken(nextSession?.access_token ?? null)
setLoading(false)
})
return () => {
mounted = false
subscription.unsubscribe()
}
}, [])
const value = useMemo<AuthContextValue>(() => {
const displayName = getDisplayName(user)
const initials = getInitials(displayName)
const avatarUrl = getAvatarUrl(user)
return {
user,
session,
loading,
isAuthenticated: Boolean(user),
displayName,
initials,
avatarUrl,
signInWithPassword: async ({ email, password }) => {
const { error } = await supabase.auth.signInWithPassword({
email,
password,
})
if (error) {
throw error
}
},
signUp: async ({ email, password, fullName }) => {
const { data, error } = await supabase.auth.signUp({
email,
password,
options: {
data: {
full_name: fullName,
name: fullName,
},
},
})
if (error) {
throw error
}
return {
requiresEmailConfirmation: !data.session,
}
},
signOut: async () => {
const { error } = await supabase.auth.signOut()
if (error) {
throw error
}
},
}
}, [loading, session, user])
return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>
}
export function useAuth() {
const context = useContext(AuthContext)
if (!context) {
throw new Error('useAuth must be used within AuthProvider')
}
return context
}