/** * Auth context and hook for managing current user state. * * Provides a React context and custom hook to access and manage * the current authenticated user throughout the application. */ import { createContext, useContext, useState, useEffect } from 'react'; import type { ReactNode } from 'react'; import { fetchCurrentUser, login as apiLogin, logout as apiLogout } from './apiAuth'; import type { User } from './apiAuth'; interface AuthContextType { user: User | null; loading: boolean; error: string | null; login: (username: string, password: string) => Promise; logout: () => Promise; refreshUser: () => Promise; isAuthenticated: boolean; } const AuthContext = createContext(undefined); interface AuthProviderProps { children: ReactNode; } /** * Auth provider component to wrap the application. * * Usage: * * * */ export function AuthProvider({ children }: AuthProviderProps) { const [user, setUser] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); // Fetch current user on mount useEffect(() => { refreshUser(); }, []); const refreshUser = async () => { try { setLoading(true); setError(null); const currentUser = await fetchCurrentUser(); setUser(currentUser); } catch (err) { // Not authenticated or error - this is okay setUser(null); // Only set error if it's not a 401 (not authenticated is expected) if (err instanceof Error && !err.message.includes('Not authenticated')) { setError(err.message); } } finally { setLoading(false); } }; const login = async (username: string, password: string) => { try { setLoading(true); setError(null); const response = await apiLogin(username, password); setUser(response.user as User); } catch (err) { const errorMessage = err instanceof Error ? err.message : 'Login failed'; setError(errorMessage); throw err; // Re-throw so the caller can handle it } finally { setLoading(false); } }; const logout = async () => { try { setLoading(true); setError(null); await apiLogout(); setUser(null); } catch (err) { const errorMessage = err instanceof Error ? err.message : 'Logout failed'; setError(errorMessage); throw err; } finally { setLoading(false); } }; const value: AuthContextType = { user, loading, error, login, logout, refreshUser, isAuthenticated: user !== null, }; return {children}; } /** * Custom hook to access auth context. * * Usage: * const { user, login, logout, isAuthenticated } = useAuth(); * * @returns AuthContextType * @throws Error if used outside AuthProvider */ export function useAuth(): AuthContextType { const context = useContext(AuthContext); if (context === undefined) { throw new Error('useAuth must be used within an AuthProvider'); } return context; } /** * Convenience hook to get just the current user. * * Usage: * const user = useCurrentUser(); */ export function useCurrentUser(): User | null { const { user } = useAuth(); return user; } /** * Convenience hook to check if user is authenticated. * * Usage: * const isAuthenticated = useIsAuthenticated(); */ export function useIsAuthenticated(): boolean { const { isAuthenticated } = useAuth(); return isAuthenticated; }