Files
AnchorOS/lib/auth/context.tsx
Marco Gallegos 0f3de32899 🚀 FASE 4 COMPLETADO: Comentarios auditables + Calendario funcional + Gestión staff/recursos
 COMENTARIOS AUDITABLES IMPLEMENTADOS:
- 80+ archivos con JSDoc completo para auditoría manual
- APIs críticas con validaciones business/security/performance
- Componentes con reglas de negocio documentadas
- Funciones core con edge cases y validaciones

 CALENDARIO MULTI-COLUMNA FUNCIONAL (95%):
- Drag & drop con reprogramación automática
- Filtros por sucursal/staff, tiempo real
- Indicadores de conflictos y disponibilidad
- APIs completas con validaciones de colisión

 GESTIÓN OPERATIVA COMPLETA:
- CRUD staff: APIs + componente con validaciones
- CRUD recursos: APIs + componente con disponibilidad
- Autenticación completa con middleware seguro
- Auditoría completa en todas las operaciones

 DOCUMENTACIÓN ACTUALIZADA:
- TASKS.md: FASE 4 95% completado
- README.md: Estado actual y funcionalidades
- API.md: 40+ endpoints documentados

 SEGURIDAD Y VALIDACIONES:
- RLS policies documentadas en comentarios
- Business rules validadas manualmente
- Performance optimizations anotadas
- Error handling completo

Próximos: Nómina/POS/CRM avanzado (FASE 4 final)
2026-01-17 15:31:13 -06:00

147 lines
4.5 KiB
TypeScript

'use client'
import { createContext, useContext, useEffect, useState, ReactNode } from 'react'
import { usePathname } from 'next/navigation'
import { User, Session } from '@supabase/supabase-js'
import { supabase } from '@/lib/supabase/client'
type AuthContextType = {
user: User | null
session: Session | null
loading: boolean
signIn: (email: string) => Promise<{ error: any }>
signInWithPassword: (email: string, password: string) => Promise<{ error: any }>
signOut: () => Promise<void>
}
const AuthContext = createContext<AuthContextType | undefined>(undefined)
/**
* @description Authentication provider managing Supabase auth state and redirects
* @param {Object} props - React children to render within auth context
* @returns {JSX.Element} AuthContext provider with authentication state
* @audit SECURITY: Handles session persistence and automatic refresh
* @audit SECURITY: Implements bidirectional redirects (login ↔ protected routes)
* @audit Validate: Session state synchronized with Supabase auth changes
* @audit Validate: Protected routes redirect to login when unauthenticated
* @audit PERFORMANCE: Auth state changes trigger immediate UI updates
*/
export function AuthProvider({ children }: { children: ReactNode }) {
const [user, setUser] = useState<User | null>(null)
const [session, setSession] = useState<Session | null>(null)
const [loading, setLoading] = useState(true)
const pathname = usePathname()
useEffect(() => {
const checkSession = async () => {
try {
const { data: { session }, error } = await supabase.auth.getSession()
if (error) {
console.error('Error getting session:', error)
// If there's an auth error, clear any stale session data
setSession(null)
setUser(null)
} else {
setSession(session)
setUser(session?.user ?? null)
}
} catch (err) {
console.error('Unexpected error getting session:', err)
setSession(null)
setUser(null)
} finally {
setLoading(false)
}
}
checkSession()
// Listen for auth state changes
const { data: { subscription } } = supabase.auth.onAuthStateChange(async (event, session) => {
console.log('Auth state change:', event, session?.user?.email)
setSession(session)
setUser(session?.user ?? null)
setLoading(false)
})
return () => {
subscription.unsubscribe()
}
}, [])
// Handle authentication redirects
useEffect(() => {
if (loading) return
const isLoginPage = pathname === '/aperture/login'
const isProtectedRoute = pathname?.startsWith('/aperture') && !isLoginPage
if (user) {
// User is authenticated
if (isLoginPage) {
// Redirect from login page to dashboard
console.log('AuthProvider: Redirecting authenticated user from login to /aperture')
window.location.replace('/aperture') // Use replace to avoid back button issues
}
} else {
// User is not authenticated
if (isProtectedRoute) {
// Redirect to login for protected routes
console.log('AuthProvider: Redirecting unauthenticated user to /aperture/login - Path:', pathname)
window.location.replace('/aperture/login') // Use replace to avoid back button issues
}
}
}, [user, loading, pathname])
const signIn = async (email: string) => {
const { error } = await supabase.auth.signInWithOtp({
email,
options: {
emailRedirectTo: `${window.location.origin}/booking`,
},
})
return { error }
}
const signInWithPassword = async (email: string, password: string) => {
const { error } = await supabase.auth.signInWithPassword({
email,
password,
})
// Don't manually update state here - the onAuthStateChange listener will handle it
return { error }
}
const signOut = async () => {
const { error } = await supabase.auth.signOut()
if (error) {
console.error('Error signing out:', error)
}
setUser(null)
setSession(null)
setLoading(false)
}
const value = {
user,
session,
loading,
signIn,
signInWithPassword,
signOut,
}
return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>
}
/**
* useAuth hook that returns current authentication context.
*/
export function useAuth() {
const context = useContext(AuthContext)
if (context === undefined) {
throw new Error('useAuth must be used within an AuthProvider')
}
return context
}