diff options
Diffstat (limited to 'frontend')
| -rw-r--r-- | frontend/src/components/Navigation.tsx | 2 | ||||
| -rw-r--r-- | frontend/src/contexts/AuthContext.tsx | 55 | ||||
| -rw-r--r-- | frontend/src/pages/Login.tsx | 17 |
3 files changed, 47 insertions, 27 deletions
diff --git a/frontend/src/components/Navigation.tsx b/frontend/src/components/Navigation.tsx index 6ae493f..4d41d32 100644 --- a/frontend/src/components/Navigation.tsx +++ b/frontend/src/components/Navigation.tsx @@ -34,7 +34,7 @@ export function Navigation() { type="button" onClick={handleLogout} className="flex items-center space-x-2 text-gray-600 hover:text-gray-900" - title={`Logout (${user.username})`} + title="Logout" > <FontAwesomeIcon icon={faRightFromBracket} /> <span className="hidden sm:inline">Logout</span> diff --git a/frontend/src/contexts/AuthContext.tsx b/frontend/src/contexts/AuthContext.tsx index fc6b237..e43a1ec 100644 --- a/frontend/src/contexts/AuthContext.tsx +++ b/frontend/src/contexts/AuthContext.tsx @@ -8,19 +8,20 @@ import { import { useMutation, useQuery } from "urql"; import { GetCurrentUserDocument, + type GetCurrentUserQuery, LoginDocument, LogoutDocument, } from "../graphql/generated/graphql"; -interface User { - id: string; - username: string; -} +type User = NonNullable<GetCurrentUserQuery["currentUser"]>; + +type LoginResult = { success: true } | { success: false; error: string }; interface AuthContextType { user: User | null; isLoading: boolean; - login: (username: string, password: string) => Promise<boolean>; + error: string | null; + login: (username: string, password: string) => Promise<LoginResult>; logout: () => Promise<void>; } @@ -29,6 +30,7 @@ const AuthContext = createContext<AuthContextType | undefined>(undefined); export function AuthProvider({ children }: { children: ReactNode }) { const [user, setUser] = useState<User | null>(null); const [isLoading, setIsLoading] = useState(true); + const [error, setError] = useState<string | null>(null); const [, executeLogin] = useMutation(LoginDocument); const [, executeLogout] = useMutation(LogoutDocument); @@ -40,49 +42,72 @@ export function AuthProvider({ children }: { children: ReactNode }) { useEffect(() => { if (currentUserResult.data?.currentUser) { setUser(currentUserResult.data.currentUser); + setError(null); } else { setUser(null); } + + if (currentUserResult.error) { + setError(currentUserResult.error.message); + } + if (!currentUserResult.fetching) { setIsLoading(false); } - }, [currentUserResult.data, currentUserResult.fetching]); + }, [ + currentUserResult.data, + currentUserResult.fetching, + currentUserResult.error, + ]); const login = async ( username: string, password: string, - ): Promise<boolean> => { + ): Promise<LoginResult> => { + setError(null); + try { const result = await executeLogin({ username, password }); + if (result.error) { + const errorMessage = + result.error.graphQLErrors[0]?.message || result.error.message; + setError(errorMessage); + return { success: false, error: errorMessage }; + } + if (result.data?.login?.user) { - setUser(result.data.login.user); // Refetch CurrentUser query to ensure session is established reexecuteCurrentUser({ requestPolicy: "network-only" }); - return true; + return { success: true }; } - return false; + const errorMessage = "Invalid username or password"; + setError(errorMessage); + return { success: false, error: errorMessage }; } catch (error) { + const errorMessage = + error instanceof Error ? error.message : "An unknown error occurred"; console.error("Login failed:", error); - return false; + setError(errorMessage); + return { success: false, error: errorMessage }; } }; const logout = async () => { try { await executeLogout({}); + // Refetch CurrentUser query to ensure session is cleared + reexecuteCurrentUser({ requestPolicy: "network-only" }); } catch (error) { console.error("Logout failed:", error); - } finally { - setUser(null); - // Refetch CurrentUser query to ensure session is cleared + // Even on error, refetch to get the latest state reexecuteCurrentUser({ requestPolicy: "network-only" }); } }; return ( - <AuthContext.Provider value={{ user, isLoading, login, logout }}> + <AuthContext.Provider value={{ user, isLoading, error, login, logout }}> {children} </AuthContext.Provider> ); diff --git a/frontend/src/pages/Login.tsx b/frontend/src/pages/Login.tsx index 5703047..277488e 100644 --- a/frontend/src/pages/Login.tsx +++ b/frontend/src/pages/Login.tsx @@ -15,18 +15,13 @@ export function Login() { setError(""); setIsLoading(true); - try { - const success = await login(username, password); - if (success) { - setLocation("/"); - } else { - setError("Invalid username or password"); - } - } catch (_err) { - setError("An error occurred during login"); - } finally { - setIsLoading(false); + const result = await login(username, password); + if (result.success) { + setLocation("/"); + } else { + setError(result.error); } + setIsLoading(false); }; return ( |
