import { create } from 'zustand'; import type { Session, User, AuthChangeEvent } from '@supabase/supabase-js'; import { supabase } from '../lib/supabase'; import { signIn as authSignIn, signOut as authSignOut, getSession, refreshSession, } from '../lib/auth'; /** * Auth store state interface */ interface AuthState { /** Current authenticated user */ user: User | null; /** Current session */ session: Session | null; /** Whether auth operations are in progress */ isLoading: boolean; /** Whether the store has been initialized */ isInitialized: boolean; /** Last auth error message */ error: string | null; } /** * Auth store actions interface */ interface AuthActions { /** Sign in with email and password */ signIn: (email: string, password: string) => Promise; /** Sign out the current user */ signOut: () => Promise; /** Initialize the auth store */ initialize: () => Promise; /** Refresh the current session */ refresh: () => Promise; /** Clear any auth errors */ clearError: () => void; /** Set the auth state (for internal use) */ setAuth: (user: User | null, session: Session | null) => void; } /** * Combined auth store type */ type AuthStore = AuthState & AuthActions; /** * Zustand store for authentication state management * * Usage: * ```typescript * import { useAuthStore } from './stores/authStore'; * * // In component * const { user, isLoading, signIn, signOut } = useAuthStore(); * * // Or use selectors for performance * const user = useAuthStore((state) => state.user); * const signIn = useAuthStore((state) => state.signIn); * ``` */ export const useAuthStore = create((set, get) => ({ // Initial state user: null, session: null, isLoading: true, isInitialized: false, error: null, /** * Sign in with email and password */ signIn: async (email: string, password: string): Promise => { set({ isLoading: true, error: null }); const result = await authSignIn(email, password); if (result.success && result.user && result.session) { set({ user: result.user, session: result.session, isLoading: false, error: null, }); return true; } set({ isLoading: false, error: result.error || 'Sign in failed', }); return false; }, /** * Sign out the current user */ signOut: async (): Promise => { set({ isLoading: true, error: null }); const result = await authSignOut(); if (result.success) { set({ user: null, session: null, isLoading: false, error: null, }); } else { set({ isLoading: false, error: result.error || 'Sign out failed', }); } }, /** * Initialize the auth store by checking for existing session */ initialize: async (): Promise => { // Prevent re-initialization if (get().isInitialized) { return; } try { const { session, error } = await getSession(); if (error) { console.warn('Auth initialization warning:', error); } set({ user: session?.user ?? null, session, isLoading: false, isInitialized: true, error: null, }); } catch (error) { console.error('Auth initialization error:', error); set({ user: null, session: null, isLoading: false, isInitialized: true, error: error instanceof Error ? error.message : 'Failed to initialize auth', }); } }, /** * Refresh the current session */ refresh: async (): Promise => { const { session } = get(); if (!session) return; try { const result = await refreshSession(); if (result.session) { set({ session: result.session, user: result.session.user, }); } else if (result.error) { console.warn('Session refresh warning:', result.error); } } catch (error) { console.error('Session refresh error:', error); } }, /** * Clear any auth errors */ clearError: (): void => { set({ error: null }); }, /** * Set the auth state (for internal use by listeners) */ setAuth: (user: User | null, session: Session | null): void => { set({ user, session, isLoading: false }); }, })); /** * Setup auth state listener * Should be called once when the app initializes */ export function setupAuthListener(): () => void { const { data: { subscription }, } = supabase.auth.onAuthStateChange( (event: AuthChangeEvent, session: Session | null) => { console.log('Auth state changed:', event); const { setAuth } = useAuthStore.getState(); switch (event) { case 'SIGNED_IN': case 'TOKEN_REFRESHED': case 'USER_UPDATED': setAuth(session?.user ?? null, session); break; case 'SIGNED_OUT': setAuth(null, null); break; default: break; } } ); return () => { subscription.unsubscribe(); }; } /** * Selector hooks for common auth state */ export const useUser = () => useAuthStore((state) => state.user); export const useSession = () => useAuthStore((state) => state.session); export const useIsLoading = () => useAuthStore((state) => state.isLoading); export const useIsInitialized = () => useAuthStore((state) => state.isInitialized); export const useAuthError = () => useAuthStore((state) => state.error); export const useIsAuthenticated = () => useAuthStore((state) => state.session !== null);