init code for the trainer app

This commit is contained in:
Cipher Vance
2026-01-25 09:56:41 -06:00
parent b29d7481e7
commit 9eab5ed98b
47 changed files with 13572 additions and 25 deletions

View File

@@ -0,0 +1,108 @@
import { createContext, useContext, useState, useEffect, useCallback, type ReactNode } from 'react';
import {
login as apiLogin,
logout as apiLogout,
getProfile,
isAuthenticated as checkAuth,
} from '../services/api';
import type { UserProfile } from '../types/api';
interface AuthContextValue {
isAuthenticated: boolean;
isLoading: boolean;
profile: UserProfile | null;
error: string | null;
login: (username: string, password: string) => Promise<boolean>;
logout: () => void;
refreshProfile: () => Promise<void>;
clearError: () => void;
}
const AuthContext = createContext<AuthContextValue | null>(null);
export function AuthProvider({ children }: { children: ReactNode }) {
const [isAuthenticated, setIsAuthenticated] = useState(checkAuth());
const [isLoading, setIsLoading] = useState(true);
const [profile, setProfile] = useState<UserProfile | null>(null);
const [error, setError] = useState<string | null>(null);
const refreshProfile = useCallback(async () => {
if (!checkAuth()) {
setProfile(null);
setIsAuthenticated(false);
return;
}
try {
const userProfile = await getProfile();
setProfile(userProfile);
setIsAuthenticated(true);
} catch (err) {
console.error('Failed to fetch profile:', err);
setProfile(null);
setIsAuthenticated(false);
}
}, []);
useEffect(() => {
const init = async () => {
setIsLoading(true);
await refreshProfile();
setIsLoading(false);
};
init();
}, [refreshProfile]);
const login = useCallback(async (username: string, password: string): Promise<boolean> => {
setIsLoading(true);
setError(null);
try {
await apiLogin({ username, password });
await refreshProfile();
setIsAuthenticated(true);
setIsLoading(false);
return true;
} catch (err) {
const message = err instanceof Error ? err.message : 'Login failed';
setError(message);
setIsLoading(false);
return false;
}
}, [refreshProfile]);
const logout = useCallback(() => {
apiLogout();
setProfile(null);
setIsAuthenticated(false);
}, []);
const clearError = useCallback(() => {
setError(null);
}, []);
return (
<AuthContext.Provider
value={{
isAuthenticated,
isLoading,
profile,
error,
login,
logout,
refreshProfile,
clearError,
}}
>
{children}
</AuthContext.Provider>
);
}
export function useAuth(): AuthContextValue {
const context = useContext(AuthContext);
if (!context) {
throw new Error('useAuth must be used within an AuthProvider');
}
return context;
}