feat: agregar sistema de autenticación con usuarios y roles
- Agregar Login con autenticación por DNI y contraseña - Crear tabla de usuarios con roles (admin, médico, enfermero) - Implementar gestión de usuarios (CRUD) para administradores - Agregar control de permisos basado en roles en el menú - Persistir estado de sesión en backend - Inicializar usuario admin por defecto (DNI: 12345678, pass: admin123) - Corregir inicialización de arrays en el store para evitar errores al cargar estado
This commit is contained in:
@@ -12,7 +12,9 @@ import type {
|
||||
Interconsulta,
|
||||
ATB,
|
||||
Indicacion,
|
||||
Vista
|
||||
Vista,
|
||||
Usuario,
|
||||
RolUsuario
|
||||
} from '@/types';
|
||||
|
||||
// Fallback UUID generator for non-secure contexts (HTTP without localhost)
|
||||
@@ -44,10 +46,13 @@ interface HospitalState {
|
||||
movimientosIndicaciones: any[];
|
||||
vistaActual: Vista;
|
||||
currentInternacionId?: string | null;
|
||||
usuarios: Usuario[];
|
||||
currentUser: Usuario | null;
|
||||
isAuthenticated: boolean;
|
||||
}
|
||||
|
||||
|
||||
const API_BASE = import.meta.env.VITE_API_URL ?? `${window.location.origin}/api`;
|
||||
const API_BASE = 'http://localhost:4001/api';
|
||||
|
||||
const defaultState = (): HospitalState => ({
|
||||
pacientes: [],
|
||||
@@ -63,7 +68,10 @@ const defaultState = (): HospitalState => ({
|
||||
vistaActual: 'dashboard',
|
||||
indicaciones: [],
|
||||
movimientosIndicaciones: [],
|
||||
camas: []
|
||||
camas: [],
|
||||
usuarios: [],
|
||||
currentUser: null,
|
||||
isAuthenticated: false,
|
||||
});
|
||||
|
||||
export function useHospitalStore() {
|
||||
@@ -79,8 +87,15 @@ export function useHospitalStore() {
|
||||
if (!res.ok) throw new Error('no state');
|
||||
const body = await res.json();
|
||||
if (mounted && body) {
|
||||
const normalized = body as HospitalState;
|
||||
normalized.movimientosIndicaciones = normalized.movimientosIndicaciones || [];
|
||||
const defaults = defaultState();
|
||||
const normalized = {
|
||||
...defaults,
|
||||
...body,
|
||||
estudiosComplementarios: body.estudiosComplementarios || [],
|
||||
interconsultas: body.interconsultas || [],
|
||||
atb: body.atb || [],
|
||||
movimientosIndicaciones: body.movimientosIndicaciones || [],
|
||||
};
|
||||
setState(normalized);
|
||||
}
|
||||
} catch (err) {
|
||||
@@ -542,6 +557,93 @@ const getEstadisticas = useCallback(() => {
|
||||
setState(prev => ({ ...prev, currentInternacionId: id ?? null }));
|
||||
}, []);
|
||||
|
||||
// Auth functions
|
||||
const login = useCallback(async (dni: string, password: string) => {
|
||||
try {
|
||||
const res = await fetch(`${API_BASE}/auth/login`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ dni, password }),
|
||||
});
|
||||
if (!res.ok) {
|
||||
const err = await res.json();
|
||||
throw new Error(err.error || 'Error en autenticación');
|
||||
}
|
||||
const user = await res.json();
|
||||
setState(prev => ({ ...prev, currentUser: user, isAuthenticated: true }));
|
||||
return user;
|
||||
} catch (err) {
|
||||
throw err;
|
||||
}
|
||||
}, []);
|
||||
|
||||
const logout = useCallback(() => {
|
||||
setState(prev => {
|
||||
const newState = { ...prev, currentUser: null, isAuthenticated: false };
|
||||
fetch(`${API_BASE}/state`, {
|
||||
method: 'PUT',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ ...newState }),
|
||||
}).catch(() => {});
|
||||
return newState;
|
||||
});
|
||||
}, []);
|
||||
|
||||
const changePassword = useCallback(async (dni: string, oldPassword: string, newPassword: string) => {
|
||||
const res = await fetch(`${API_BASE}/auth/change-password`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ dni, oldPassword, newPassword }),
|
||||
});
|
||||
if (!res.ok) {
|
||||
const err = await res.json();
|
||||
throw new Error(err.error || 'Error al cambiar contraseña');
|
||||
}
|
||||
return true;
|
||||
}, []);
|
||||
|
||||
const updateEmail = useCallback(async (dni: string, newEmail: string) => {
|
||||
const res = await fetch(`${API_BASE}/auth/update-email`, {
|
||||
method: 'PUT',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ dni, newEmail }),
|
||||
});
|
||||
if (!res.ok) {
|
||||
const err = await res.json();
|
||||
throw new Error(err.error || 'Error al actualizar email');
|
||||
}
|
||||
return true;
|
||||
}, []);
|
||||
|
||||
const hasPermission = useCallback((permission: 'read' | 'write', section: string): boolean => {
|
||||
const user = state.currentUser;
|
||||
if (!user) return false;
|
||||
if (user.rol === 'admin') return true;
|
||||
if (user.rol === 'enfermero') {
|
||||
if (section === 'glucemias' || section === 'signosvitales') return permission === 'read' || permission === 'write';
|
||||
return permission === 'read';
|
||||
}
|
||||
if (user.rol === 'medico') {
|
||||
if (section === 'miArea') return permission === 'read' || permission === 'write';
|
||||
return permission === 'read';
|
||||
}
|
||||
return false;
|
||||
}, [state.currentUser]);
|
||||
|
||||
const canAccessInternacion = useCallback((internacionId: string): boolean => {
|
||||
const user = state.currentUser;
|
||||
if (!user) return false;
|
||||
const rol = user.rol as string;
|
||||
if (rol === 'admin') return true;
|
||||
if (rol === 'medico' && user.areaId) {
|
||||
const internacion = state.internaciones.find(i => i.id === internacionId);
|
||||
if (!internacion) return false;
|
||||
const cama = state.camas.find(c => c.id === internacion.camaId);
|
||||
return cama?.areaId === user.areaId;
|
||||
}
|
||||
return rol === 'admin';
|
||||
}, [state.currentUser, state.internaciones, state.camas]);
|
||||
|
||||
return {
|
||||
...state,
|
||||
isLoaded,
|
||||
@@ -594,5 +696,11 @@ const getEstadisticas = useCallback(() => {
|
||||
actualizarLaboratorio,
|
||||
setCurrentInternacion,
|
||||
getEstadisticas,
|
||||
login,
|
||||
logout,
|
||||
changePassword,
|
||||
updateEmail,
|
||||
hasPermission,
|
||||
canAccessInternacion,
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user