126 lines
4.0 KiB
TypeScript
126 lines
4.0 KiB
TypeScript
import React, { createContext, useContext, useState, useEffect, useCallback } from 'react';
|
|
import type { AppUser } from '../types';
|
|
import { setApiToken, setApiOrganizationId, getBaseUrl } from '../services/api';
|
|
|
|
const API_URL = getBaseUrl();
|
|
|
|
export interface AuthContextType {
|
|
appUser: AppUser | null;
|
|
isLoading: boolean;
|
|
error: string | null;
|
|
token: string | null;
|
|
login: (token: string, user: AppUser) => void;
|
|
logout: () => void;
|
|
isAdmin: () => boolean;
|
|
isUser: () => boolean;
|
|
isGuest: () => boolean;
|
|
isDeveloper: () => boolean;
|
|
canEdit: () => boolean;
|
|
refetchUser: () => Promise<void>;
|
|
}
|
|
|
|
export const AuthContext = createContext<AuthContextType | undefined>(undefined);
|
|
|
|
export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
|
|
const [appUser, setAppUser] = useState<AppUser | null>(null);
|
|
const [token, setToken] = useState<string | null>(localStorage.getItem('jwt_token'));
|
|
const [isLoading, setIsLoading] = useState(true);
|
|
const [error, setError] = useState<string | null>(null);
|
|
|
|
// Initial load: se tem token, setar no interceptor e buscar dados do usuário
|
|
useEffect(() => {
|
|
if (token) {
|
|
setApiToken(token);
|
|
refetchUser();
|
|
} else {
|
|
setIsLoading(false);
|
|
}
|
|
}, [token]);
|
|
|
|
const login = useCallback((newToken: string, user: AppUser) => {
|
|
localStorage.setItem('jwt_token', newToken);
|
|
setToken(newToken);
|
|
setAppUser(user);
|
|
setApiToken(newToken);
|
|
|
|
// Se a organização existir, setar o header
|
|
if (user.organizationId) {
|
|
setApiOrganizationId(user.organizationId);
|
|
}
|
|
}, []);
|
|
|
|
const logout = useCallback(() => {
|
|
localStorage.removeItem('jwt_token');
|
|
setToken(null);
|
|
setAppUser(null);
|
|
setApiToken(null);
|
|
setApiOrganizationId(null);
|
|
}, []);
|
|
|
|
const refetchUser = useCallback(async () => {
|
|
if (!token) return;
|
|
setIsLoading(true);
|
|
try {
|
|
const response = await fetch(`${API_URL}/auth/me`, {
|
|
headers: {
|
|
'Authorization': `Bearer ${token}`
|
|
},
|
|
});
|
|
|
|
if (response.ok) {
|
|
const userData = await response.json();
|
|
setAppUser(userData);
|
|
if (userData.organizationId) {
|
|
setApiOrganizationId(userData.organizationId);
|
|
}
|
|
} else {
|
|
// Token inválido ou expirado
|
|
logout();
|
|
}
|
|
} catch (err) {
|
|
console.error('Error refetching user:', err);
|
|
setError('Falha na comunicação de autenticação.');
|
|
} finally {
|
|
setIsLoading(false);
|
|
}
|
|
}, [token, logout]);
|
|
|
|
const isDeveloper = useCallback(() => {
|
|
return appUser?.email === 'admtracksteel@gmail.com';
|
|
}, [appUser]);
|
|
|
|
const isAdmin = useCallback(() => appUser?.role === 'admin' || isDeveloper(), [appUser, isDeveloper]);
|
|
const isUser = useCallback(() => appUser?.role === 'user' || isAdmin(), [appUser, isAdmin]);
|
|
const isGuest = useCallback(() => appUser?.role === 'guest' && !isDeveloper(), [appUser, isDeveloper]);
|
|
const canEdit = useCallback(() => (appUser?.role !== 'guest' && appUser !== null) || isDeveloper(), [appUser, isDeveloper]);
|
|
|
|
return (
|
|
<AuthContext.Provider
|
|
value={{
|
|
appUser,
|
|
isLoading,
|
|
error,
|
|
token,
|
|
login,
|
|
logout,
|
|
isAdmin,
|
|
isUser,
|
|
isGuest,
|
|
isDeveloper,
|
|
canEdit,
|
|
refetchUser,
|
|
}}
|
|
>
|
|
{children}
|
|
</AuthContext.Provider>
|
|
);
|
|
};
|
|
|
|
export const useAuth = () => {
|
|
const context = useContext(AuthContext);
|
|
if (!context) {
|
|
throw new Error('useAuth must be used within an AuthProvider');
|
|
}
|
|
return context;
|
|
};
|