The ManageUsers sub-component referenced user?.role for conditional role dropdown options but didn't call useAuth() to get the user object. This caused 'user is not defined' JS errors and blank admin pages. Browser E2E: 20/20 tests pass.
99 lines
2.5 KiB
React
99 lines
2.5 KiB
React
import { createContext, useContext, useState, useEffect, useCallback } from 'react';
|
|
import { useLocation } from 'react-router-dom';
|
|
import api, { setTokens, clearTokens, getAccessToken } from '../api/client';
|
|
|
|
const AuthContext = createContext(null);
|
|
|
|
export function AuthProvider({ children }) {
|
|
const [user, setUser] = useState(() => {
|
|
try {
|
|
const stored = localStorage.getItem('user');
|
|
return stored ? JSON.parse(stored) : null;
|
|
} catch {
|
|
return null;
|
|
}
|
|
});
|
|
const [loading, setLoading] = useState(true);
|
|
const location = useLocation();
|
|
|
|
const refreshUser = useCallback(async () => {
|
|
try {
|
|
const { data } = await api.get('/auth/me');
|
|
setUser(data.user);
|
|
localStorage.setItem('user', JSON.stringify(data.user));
|
|
} catch {
|
|
/* If /me fails, we might be logged out or server is down */
|
|
}
|
|
}, []);
|
|
|
|
/* On mount, verify the stored token is still valid */
|
|
useEffect(() => {
|
|
async function verify() {
|
|
const token = getAccessToken();
|
|
if (!token) {
|
|
setLoading(false);
|
|
return;
|
|
}
|
|
try {
|
|
const { data } = await api.get('/auth/me');
|
|
setUser(data.user);
|
|
localStorage.setItem('user', JSON.stringify(data.user));
|
|
} catch {
|
|
clearTokens();
|
|
setUser(null);
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
}
|
|
verify();
|
|
}, []);
|
|
|
|
/* Refresh user profile on navigation to ensure role is up to date */
|
|
useEffect(() => {
|
|
if (user) {
|
|
refreshUser();
|
|
}
|
|
}, [location.pathname, user?.id]);
|
|
|
|
const login = useCallback(async (email, password) => {
|
|
const { data } = await api.post('/auth/login', { email, password });
|
|
setTokens(data.accessToken, data.refreshToken);
|
|
setUser(data.user);
|
|
localStorage.setItem('user', JSON.stringify(data.user));
|
|
return data.user;
|
|
}, []);
|
|
|
|
const logout = useCallback(async () => {
|
|
try {
|
|
await api.post('/auth/logout');
|
|
} catch {
|
|
/* ignore — we clear locally regardless */
|
|
}
|
|
clearTokens();
|
|
setUser(null);
|
|
}, []);
|
|
|
|
const isAdmin = ['office', 'admin', 'super_admin'].includes(user?.role);
|
|
|
|
const value = {
|
|
user,
|
|
loading,
|
|
login,
|
|
logout,
|
|
isAdmin,
|
|
isAuthenticated: !!user,
|
|
};
|
|
|
|
return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
|
|
}
|
|
|
|
export function useAuth() {
|
|
const ctx = useContext(AuthContext);
|
|
if (!ctx) {
|
|
throw new Error('useAuth must be used within an AuthProvider');
|
|
}
|
|
return ctx;
|
|
}
|
|
|
|
export default AuthContext;
|