| import { createContext, useContext, useState, useCallback, useEffect } from 'react' |
| import { api, API_BASE } from '../api' |
|
|
| const AuthContext = createContext(null) |
|
|
| const TOKEN_KEY = 'omnidiag_token' |
| const USER_KEY = 'omnidiag_user' |
|
|
| export function AuthProvider({ children }) { |
| const [token, setToken] = useState(() => { |
| const stored = localStorage.getItem(TOKEN_KEY) |
| if (stored) api.setToken(stored) |
| return stored |
| }) |
| const [user, setUser] = useState(() => { |
| try { return JSON.parse(localStorage.getItem(USER_KEY) || 'null') } |
| catch { return null } |
| }) |
| const [loginError, setLoginError] = useState(null) |
| const [loginLoading, setLoginLoading] = useState(false) |
|
|
| |
| useEffect(() => { |
| if (!token) return |
| fetch(`${API_BASE}/auth/me`, { headers: { Authorization: `Bearer ${token}` } }) |
| .then(r => r.ok ? r.json() : Promise.reject()) |
| .then(u => { setUser(u); localStorage.setItem(USER_KEY, JSON.stringify(u)) }) |
| .catch(() => { setToken(null); setUser(null); localStorage.removeItem(TOKEN_KEY); localStorage.removeItem(USER_KEY) }) |
| }, []) |
|
|
| const login = useCallback(async (email, password) => { |
| setLoginError(null) |
| setLoginLoading(true) |
| try { |
| const res = await fetch(`${API_BASE}/auth/login`, { |
| method: 'POST', |
| headers: { 'Content-Type': 'application/json' }, |
| body: JSON.stringify({ email, password }), |
| }) |
| const data = await res.json() |
| if (!res.ok) throw new Error(data?.error || data?.detail || 'Login failed') |
|
|
| const accessToken = data.access_token |
| setToken(accessToken) |
| api.setToken(accessToken) |
| localStorage.setItem(TOKEN_KEY, accessToken) |
|
|
| |
| const meRes = await fetch(`${API_BASE}/auth/me`, { headers: { Authorization: `Bearer ${accessToken}` } }) |
| const me = await meRes.json() |
| setUser(me) |
| localStorage.setItem(USER_KEY, JSON.stringify(me)) |
| return true |
| } catch (e) { |
| setLoginError(e.message) |
| return false |
| } finally { |
| setLoginLoading(false) |
| } |
| }, []) |
|
|
| const logout = useCallback(() => { |
| fetch(`${API_BASE}/auth/logout`, { |
| method: 'POST', |
| headers: { Authorization: `Bearer ${token}` }, |
| }).catch(() => {}) |
| setToken(null) |
| setUser(null) |
| api.setToken(null) |
| localStorage.removeItem(TOKEN_KEY) |
| localStorage.removeItem(USER_KEY) |
| }, [token]) |
|
|
| const isAdmin = user?.roles?.some(r => ['admin', 'super_admin'].includes(typeof r === 'string' ? r : r?.name)) ?? false |
|
|
| return ( |
| <AuthContext.Provider value={{ token, user, isAdmin, login, logout, loginError, loginLoading }}> |
| {children} |
| </AuthContext.Provider> |
| ) |
| } |
|
|
| export function useAuth() { |
| const ctx = useContext(AuthContext) |
| if (!ctx) throw new Error('useAuth must be used inside AuthProvider') |
| return ctx |
| } |
|
|