Files
wolk/frontend/src/context/AuthContext.tsx
zias 49553233fe feat: migrate frontend from Vue 3 to React 18 with TanStack ecosystem
- Complete rewrite of frontend using React 18 + TypeScript in strict mode
- Implement TanStack Router for file-based routing matching URL structure
- Use TanStack Query for server state management with smart caching
- Replace Pinia stores with React Context API for auth and UI state
- Adopt Tailwind CSS + shadcn/ui components for consistent styling
- Switch from pnpm to Bun for faster package management and builds
- Configure Vite to support React, TypeScript, and modern tooling
- Create frontend.go with Go embed package for embedding dist/ in binary
- Implement comprehensive TypeScript interfaces (strict mode, no 'any' types)
- Add dark mode support throughout with Tailwind CSS dark: classes
- Set up i18n infrastructure (English translations included)
- Remove all Vue 3 code, components, stores, CSS, and assets
- Includes 18 new files with ~2000 lines of production-ready code
2026-03-16 16:13:12 +01:00

105 lines
2.4 KiB
TypeScript

import {
createContext,
useContext,
useEffect,
useState,
ReactNode,
} from "react";
import type { IUser } from "@/types";
import { auth as authAPI } from "@/api";
interface AuthContextType {
user: IUser | null;
isAuthenticated: boolean;
isLoading: boolean;
error: string | null;
login: (username: string, password: string) => Promise<void>;
logout: () => Promise<void>;
}
const AuthContext = createContext<AuthContextType | undefined>(undefined);
export function AuthProvider({ children }: { children: ReactNode }) {
const [user, setUser] = useState<IUser | null>(null);
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
// Initialize auth state on mount
useEffect(() => {
async function initAuth() {
try {
const currentUser = await authAPI.getCurrentUser();
setUser(currentUser);
setError(null);
} catch (err) {
setUser(null);
// Not an error if not authenticated — just not logged in
} finally {
setIsLoading(false);
}
}
initAuth();
// Listen for logout events from other tabs/contexts
const handleLogout = () => {
setUser(null);
};
window.addEventListener("auth:logout", handleLogout);
return () => window.removeEventListener("auth:logout", handleLogout);
}, []);
const login = async (username: string, password: string) => {
try {
setIsLoading(true);
setError(null);
await authAPI.login(username, password);
const currentUser = await authAPI.getCurrentUser();
setUser(currentUser);
} catch (err) {
const message =
err instanceof Error ? err.message : "Login failed";
setError(message);
throw err;
} finally {
setIsLoading(false);
}
};
const logout = async () => {
try {
await authAPI.logout();
setUser(null);
setError(null);
} catch (err) {
console.error("Logout error:", err);
// Clear local state even if logout fails
setUser(null);
}
};
return (
<AuthContext.Provider
value={{
user,
isAuthenticated: !!user,
isLoading,
error,
login,
logout,
}}
>
{children}
</AuthContext.Provider>
);
}
export function useAuth() {
const context = useContext(AuthContext);
if (!context) {
throw new Error("useAuth must be used within AuthProvider");
}
return context;
}