operation-cycle / teamtasker /src /context /auth-context.tsx
o134's picture
Upload full TeamTasker system with all fixes
8314cf4 verified
Raw
History Blame Contribute Delete
3.65 kB
import { useState, useEffect, createContext, useContext, ReactNode, useCallback } from "react";
export interface AuthUser {
id: number;
name: string;
email: string;
team: string;
role: string;
avatarColor: string;
createdAt: string;
}
interface AuthContextType {
user: AuthUser | null;
token: string | null;
login: (email: string, password: string) => Promise<void>;
logout: () => void;
isLoading: boolean;
error: string | null;
// Legacy compatibility for pages still using activeUser
activeUser: AuthUser | null;
setActiveUserId: (id: number | null) => void;
}
const AuthContext = createContext<AuthContextType>({
user: null,
token: null,
login: async () => {},
logout: () => {},
isLoading: false,
error: null,
activeUser: null,
setActiveUserId: () => {},
});
export function AuthProvider({ children }: { children: ReactNode }) {
const [user, setUser] = useState<AuthUser | null>(null);
const [token, setToken] = useState<string | null>(null);
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
// On mount, restore from localStorage
useEffect(() => {
const savedToken = localStorage.getItem("teamtasker_token");
const savedUser = localStorage.getItem("teamtasker_user");
if (savedToken && savedUser) {
try {
const u = JSON.parse(savedUser) as AuthUser;
setToken(savedToken);
setUser(u);
// Verify token is still valid
fetch("/api/auth/me", {
method: "POST",
headers: { Authorization: `Bearer ${savedToken}` },
}).then(async (res) => {
if (res.ok) {
const data = await res.json();
setUser(data.user);
localStorage.setItem("teamtasker_user", JSON.stringify(data.user));
} else {
// Token invalid, clear
setToken(null);
setUser(null);
localStorage.removeItem("teamtasker_token");
localStorage.removeItem("teamtasker_user");
}
}).finally(() => setIsLoading(false));
} catch {
setIsLoading(false);
}
} else {
setIsLoading(false);
}
}, []);
const login = useCallback(async (email: string, password: string) => {
setIsLoading(true);
setError(null);
try {
const res = await fetch("/api/auth/login", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ email, password }),
});
const data = await res.json();
if (!res.ok) {
setError(data.error ?? "فشل تسجيل الدخول");
return;
}
setToken(data.token);
setUser(data.user);
localStorage.setItem("teamtasker_token", data.token);
localStorage.setItem("teamtasker_user", JSON.stringify(data.user));
} catch {
setError("خطأ في الاتصال. حاول مجدداً.");
} finally {
setIsLoading(false);
}
}, []);
const logout = useCallback(() => {
setToken(null);
setUser(null);
localStorage.removeItem("teamtasker_token");
localStorage.removeItem("teamtasker_user");
}, []);
// Legacy shim — pages that still call setActiveUserId will do nothing harmful
const setActiveUserId = useCallback((_id: number | null) => {}, []);
return (
<AuthContext.Provider value={{
user,
token,
login,
logout,
isLoading,
error,
activeUser: user,
setActiveUserId,
}}>
{children}
</AuthContext.Provider>
);
}
export const useAuth = () => useContext(AuthContext);