import { createContext, useContext, useEffect, useState, useCallback } from 'react'; import type { FC, ReactNode } from 'react'; import { odooApi, type FrontendUser, type FrontendRole } from '../services/odoo'; const TOKEN_KEY = 'skeen_token'; const USER_KEY = 'skeen_user'; const ROLE_RANK: Record = { lectura: 10, medico: 50, recepcion: 50, admin: 100, }; // Visibilidad del menú por ruta. Si una ruta no está listada, se muestra a todos los roles. const MENU_ROLES: Record = { '/': ['admin', 'recepcion', 'medico', 'lectura'], '/agenda': ['admin', 'recepcion', 'medico'], '/pacientes': ['admin', 'recepcion', 'medico', 'lectura'], '/medicos': ['admin', 'recepcion'], '/servicios': ['admin', 'recepcion', 'medico', 'lectura'], '/productos': ['admin', 'recepcion', 'lectura'], '/ventas': ['admin', 'recepcion'], '/pagos': ['admin', 'recepcion'], '/monedero': ['admin', 'recepcion', 'lectura'], '/inventario': ['admin', 'recepcion', 'lectura'], '/cortes': ['admin', 'recepcion'], '/reportes': ['admin', 'recepcion', 'lectura'], '/configuracion': ['admin'], '/usuarios': ['admin'], '/cumpleanos': ['admin', 'recepcion'], '/wacrm/messages': ['admin', 'recepcion'], '/wacrm/leads': ['admin', 'recepcion'], }; interface AuthContextValue { user: FrontendUser | null; token: string | null; loading: boolean; login: (login: string, password: string) => Promise; logout: () => Promise; hasRole: (min: FrontendRole) => boolean; canSee: (href: string) => boolean; } const AuthContext = createContext(undefined); export const AuthProvider: FC<{ children: ReactNode }> = ({ children }) => { const [user, setUser] = useState(null); const [token, setToken] = useState(() => localStorage.getItem(TOKEN_KEY)); const [loading, setLoading] = useState(true); const clear = useCallback(() => { localStorage.removeItem(TOKEN_KEY); localStorage.removeItem(USER_KEY); setToken(null); setUser(null); }, []); // Validar token al arrancar useEffect(() => { let alive = true; const t = localStorage.getItem(TOKEN_KEY); if (!t) { setLoading(false); return; } // Hidratar usuario cacheado para evitar parpadeo const cached = localStorage.getItem(USER_KEY); if (cached) { try { setUser(JSON.parse(cached)); } catch { /* ignore */ } } odooApi.me() .then((res) => { if (!alive) return; if (res.status === 'success') { setUser(res.user); localStorage.setItem(USER_KEY, JSON.stringify(res.user)); } else { clear(); } }) .catch(() => { if (alive) clear(); }) .finally(() => { if (alive) setLoading(false); }); return () => { alive = false; }; }, [clear]); const login = async (login: string, password: string): Promise => { const res = await odooApi.login(login, password); if (res.status !== 'success' || !res.token) { throw new Error(res.message || 'Credenciales inválidas'); } localStorage.setItem(TOKEN_KEY, res.token); localStorage.setItem(USER_KEY, JSON.stringify(res.user)); setToken(res.token); setUser(res.user); return res.user; }; const logout = async () => { try { await odooApi.logout(); } catch { /* ignore */ } clear(); }; const hasRole = (min: FrontendRole): boolean => { if (!user) return false; return (ROLE_RANK[user.role] ?? 0) >= (ROLE_RANK[min] ?? 0); }; const canSee = (href: string): boolean => { if (!user) return false; const allowed = MENU_ROLES[href]; if (!allowed) return true; return allowed.includes(user.role); }; return ( {children} ); }; export const useAuth = (): AuthContextValue => { const ctx = useContext(AuthContext); if (!ctx) throw new Error('useAuth debe usarse dentro de AuthProvider'); return ctx; }; export const ROLE_LABELS: Record = { admin: 'Administrador', recepcion: 'Recepción', medico: 'Médico', lectura: 'Solo lectura', };