import { useState, useEffect, useCallback } from 'react'; import { toast } from 'sonner'; import { getNombreProfesional, isCamaFueraDeGrupo, computeSector, sortCamas } from '@/lib/utils'; import type { Paciente, Cama, Grupo, Internacion, Evolucion, Laboratorio, Glucemia, AcidoBase, Cultivo, EstudioComplementario, Interconsulta, ATB, Indicacion, MovimientoIndicacion, Pendiente, Vista, Usuario } from '@/types'; // Fallback UUID generator for non-secure contexts (HTTP without localhost) function generateUUID(): string { if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') { return crypto.randomUUID(); } return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => { const r = (Math.random() * 16) | 0; const v = c === 'x' ? r : (r & 0x3) | 0x8; return v.toString(16); }); } interface HospitalState { pacientes: Paciente[]; camas: Cama[]; grupos: Grupo[]; internaciones: Internacion[]; evoluciones: Evolucion[]; laboratorios: Laboratorio[]; glucemias: Glucemia[]; acidosBase: AcidoBase[]; cultivos: Cultivo[]; estudiosComplementarios: EstudioComplementario[]; interconsultas: Interconsulta[]; atb: ATB[]; indicaciones: Indicacion[]; movimientosIndicaciones: MovimientoIndicacion[]; pendientes: Pendiente[]; vistaActual: Vista; currentInternacionId?: string | null; usuarios: Usuario[]; currentUser: Usuario | null; isAuthenticated: boolean; } const API_BASE = import.meta.env.VITE_API_URL || '/api'; const defaultState = (): HospitalState => ({ pacientes: [], grupos: [], internaciones: [], evoluciones: [], laboratorios: [], glucemias: [], acidosBase: [], cultivos: [], estudiosComplementarios: [], interconsultas: [], atb: [], vistaActual: 'dashboard', indicaciones: [], movimientosIndicaciones: [], pendientes: [], camas: [], usuarios: [], currentUser: null, isAuthenticated: false, }); export function useHospitalStore() { const [state, setState] = useState(defaultState); const [isLoaded, setIsLoaded] = useState(false); // Load state from backend on mount useEffect(() => { let mounted = true; (async () => { try { const res = await fetch(`${API_BASE}/state`); if (!res.ok) throw new Error('no state'); const body = await res.json(); if (mounted && body) { const defaults = defaultState(); const storedUser = sessionStorage.getItem('hospital_user'); const storedInternacionId = sessionStorage.getItem('hospital_current_internacion_id'); const currentUser = storedUser ? JSON.parse(storedUser) : null; const normalized = { ...defaults, ...body, estudiosComplementarios: body.estudiosComplementarios || [], interconsultas: body.interconsultas || [], atb: body.atb || [], glucemias: body.glucemias || [], movimientosIndicaciones: body.movimientosIndicaciones || [], pendientes: body.pendientes || [], currentInternacionId: storedInternacionId || body.currentInternacionId || null, camas: sortCamas(body.camas || []), currentUser, isAuthenticated: !!currentUser, }; setState(normalized); } } catch { // no remote state or unreachable — keep defaults } finally { if (mounted) setIsLoaded(true); } })(); return () => { mounted = false; }; }, []); // Helper to make API calls with error handling const apiCall = useCallback(async (method: string, endpoint: string, data?: unknown) => { try { const res = await fetch(`${API_BASE}${endpoint}`, { method, headers: { 'Content-Type': 'application/json' }, body: data ? JSON.stringify(data) : undefined, }); if (!res.ok) { let errText = res.statusText; try { const errData = await res.json(); if (errData && errData.error) errText = errData.error; } catch { // ignore json parse error } throw new Error(`API error: ${errText}`); } const text = await res.text(); try { return JSON.parse(text); } catch { return { ok: true }; } } catch (err) { console.error(`API call failed: ${method} ${endpoint}`, err); throw err; } }, []); // Utility functions const getCamaGrupoId = useCallback((camaId: string): string | null => { const cama = state.camas.find(c => c.id === camaId); return cama?.grupoId || null; }, [state.camas]); const getInternacionGrupoId = useCallback((internacionId: string): string | null => { const internacion = state.internaciones.find(i => i.id === internacionId); if (!internacion) return null; return internacion.grupoId || getCamaGrupoId(internacion.camaId) || null; }, [state.internaciones, getCamaGrupoId]); const getPacienteGrupoId = useCallback((pacienteId: string): string | null => { const internacion = state.internaciones.find(i => i.pacienteId === pacienteId && i.activa); if (!internacion) { const anyInternacion = state.internaciones.find(i => i.pacienteId === pacienteId); if (anyInternacion) return getCamaGrupoId(anyInternacion.camaId); return null; } return internacion.grupoId || getCamaGrupoId(internacion.camaId) || null; }, [state.internaciones, getCamaGrupoId]); // Permission functions const canAccessGrupo = useCallback((grupoId: string | null | undefined): boolean => { const user = state.currentUser; if (!user) return false; if (user.rol === 'admin') return true; if (!grupoId) return true; if (!user.grupoId) return true; return user.grupoId === grupoId; }, [state.currentUser]); const checkGrupoPermission = useCallback((grupoId: string | null | undefined): boolean => { const allowed = canAccessGrupo(grupoId); if (!allowed) { toast.error("no pertenecen al grupo de trabajo por la que se encuentra internado el paciente"); } return allowed; }, [canAccessGrupo]); const canChangeBed = useCallback((internacionId: string): boolean => { const internacion = state.internaciones.find(i => i.id === internacionId); if (!internacion) return false; return canAccessGrupo(internacion.grupoId); }, [state.internaciones, canAccessGrupo]); const canEditInternacion = useCallback((internacionId: string): boolean => { const grupoId = getInternacionGrupoId(internacionId); return canAccessGrupo(grupoId); }, [getInternacionGrupoId, canAccessGrupo]); const canEditIngreso = useCallback((internacionId: string): boolean => { const internacion = state.internaciones.find(i => i.id === internacionId); if (!internacion) return false; const normalizeMedicoName = (name?: string) => { return (name || '').trim().toLowerCase().normalize('NFD').replace(/[\u0300-\u036f]/g, ''); }; const userProfesionalName = getNombreProfesional(state.currentUser); const grupoId = getInternacionGrupoId(internacionId); return state.currentUser?.rol === 'admin' || state.currentUser?.rol === 'medico' || canAccessGrupo(grupoId) || !internacion.medicoIngresante || (normalizeMedicoName(internacion.medicoIngresante) === normalizeMedicoName(userProfesionalName)); }, [state.internaciones, state.currentUser, getInternacionGrupoId, canAccessGrupo]); const canEditCama = useCallback((camaId: string): boolean => { const grupoId = getCamaGrupoId(camaId); return canAccessGrupo(grupoId); }, [getCamaGrupoId, canAccessGrupo]); const canEditPaciente = useCallback((pacienteId: string): boolean => { const grupoId = getPacienteGrupoId(pacienteId); return canAccessGrupo(grupoId); }, [getPacienteGrupoId, canAccessGrupo]); 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 === 'miGrupo') return permission === 'read' || permission === 'write'; return permission === 'read'; } return false; }, [state.currentUser]); // Acciones de navegación const setVista = useCallback((vista: Vista) => { setState(prev => ({ ...prev, vistaActual: vista })); }, []); // Acciones de pacientes const agregarPaciente = useCallback(async (paciente: Omit) => { const nuevoPaciente: Paciente = { ...paciente, id: generateUUID(), fechaRegistro: new Date().toISOString().split('T')[0], }; try { await apiCall('POST', '/pacientes', nuevoPaciente); setState(prev => ({ ...prev, pacientes: [...prev.pacientes, nuevoPaciente], })); return nuevoPaciente.id; } catch (err) { console.error('Error al agregar paciente:', err); throw err; } }, [apiCall]); const actualizarPaciente = useCallback(async (id: string, datos: Partial) => { const grupoId = getPacienteGrupoId(id); if (!checkGrupoPermission(grupoId)) return; try { await apiCall('PUT', `/pacientes/${id}`, datos); setState(prev => ({ ...prev, pacientes: prev.pacientes.map(p => p.id === id ? { ...p, ...datos } : p), })); } catch (err) { console.error('Error al actualizar paciente:', err); throw err; } }, [apiCall, checkGrupoPermission, getPacienteGrupoId]); const eliminarPaciente = useCallback(async (id: string) => { const grupoId = getPacienteGrupoId(id); if (!checkGrupoPermission(grupoId)) return; try { await apiCall('DELETE', `/pacientes/${id}`); setState(prev => ({ ...prev, pacientes: prev.pacientes.filter(p => p.id !== id), })); } catch (err) { console.error('Error al eliminar paciente:', err); throw err; } }, [apiCall, checkGrupoPermission, getPacienteGrupoId]); // Acciones de camas const actualizarCama = useCallback(async (id: string, datos: Partial) => { const cama = state.camas.find(c => c.id === id); if (!cama) return; const user = state.currentUser; if (user && user.rol !== 'admin') { // Must have a working group and the bed must belong to it if (!user.grupoId || cama.grupoId !== user.grupoId) { toast.error('No tiene permisos para modificar camas fuera de su grupo de trabajo'); return; } // A regular user can ONLY modify the "Tipo" of the bed. datos = { tipo: datos.tipo }; } try { if (datos.numero !== undefined) { datos.sector = computeSector(datos.numero); } await apiCall('PUT', `/camas/${id}`, datos); setState(prev => ({ ...prev, camas: sortCamas(prev.camas.map(c => c.id === id ? { ...c, ...datos } : c)), })); } catch (err) { console.error('Error al actualizar cama:', err); throw err; } }, [state, apiCall]); const agregarCama = useCallback(async (cama: Omit) => { if (state.currentUser?.rol !== 'admin') { toast.error('Solo el usuario administrador puede agregar camas'); return; } const nuevaCama: Cama = { ...cama, id: generateUUID(), sector: computeSector(cama.numero), } as Cama; try { await apiCall('POST', '/camas', nuevaCama); setState(prev => ({ ...prev, camas: sortCamas([...prev.camas, nuevaCama]), })); return nuevaCama.id; } catch (err) { console.error('Error al agregar cama:', err); throw err; } }, [state.currentUser, apiCall]); const eliminarCama = useCallback(async (id: string) => { if (state.currentUser?.rol !== 'admin') { toast.error('Solo el usuario administrador puede eliminar camas'); return; } try { // Si hay una internación activa asociada a esta cama, remover la asociación en el servidor const activeInternacion = state.internaciones.find(i => i.camaId === id && i.activa); if (activeInternacion) { await apiCall('PUT', `/internaciones/${activeInternacion.id}`, { camaId: '' }); } await apiCall('DELETE', `/camas/${id}`); setState(prev => ({ ...prev, camas: prev.camas.filter(c => c.id !== id), internaciones: prev.internaciones.map(i => i.camaId === id ? { ...i, camaId: undefined } : i), })); } catch (err) { console.error('Error al eliminar cama:', err); throw err; } }, [state.currentUser, state.internaciones, apiCall]); // Acciones de internaciones const iniciarInternacion = useCallback(async (internacion: Omit) => { if (!checkGrupoPermission(internacion.grupoId)) { return null; } const nuevaInternacion: Internacion = { ...internacion, id: generateUUID(), activa: true, }; try { // API call inmediata para crear internación await apiCall('POST', '/internaciones', nuevaInternacion); // Actualizar estado de la cama en la base de datos if (internacion.camaId) { await apiCall('PUT', `/camas/${internacion.camaId}`, { estado: 'Ocupada', pacienteId: internacion.pacienteId, internacionId: nuevaInternacion.id }); } // Actualizar estado local después del éxito setState(prev => ({ ...prev, internaciones: [...prev.internaciones, nuevaInternacion], camas: prev.camas.map(c => c.id === internacion.camaId ? { ...c, estado: 'Ocupada' as const, pacienteId: internacion.pacienteId, internacionId: nuevaInternacion.id } : c ), })); return nuevaInternacion.id; } catch (err) { console.error('Error al crear internación:', err); throw err; } }, [apiCall, checkGrupoPermission]); const finalizarInternacion = useCallback(async (internacionId: string, datos: { fechaEgreso: string; diagnosticoEgreso: string; motivoEgreso: Internacion['motivoEgreso'] }) => { const internacion = state.internaciones.find(i => i.id === internacionId); if (!internacion) return; const grupoFueraDeGrupo = state.grupos.find(a => (a.nombre || '').toLowerCase().trim() === 'fuera de grupo'); const grupoFueraDeGrupoId = grupoFueraDeGrupo?.id; const esFueraDeGrupo = !internacion.grupoId || !grupoFueraDeGrupoId || internacion.grupoId === grupoFueraDeGrupoId; const camaId = internacion.camaId; const camaObj = camaId ? state.camas.find(c => c.id === camaId) : undefined; const debeEliminarCama = camaObj ? (computeSector(camaObj.numero) === 'Fuera de Área') : esFueraDeGrupo; try { // API call inmediata para finalizar internación await apiCall('PUT', `/internaciones/${internacionId}`, { ...datos, activa: false }); // Eliminar o actualizar la cama según el área if (camaId) { if (debeEliminarCama) { // Eliminar la cama si es "Fuera de área" await apiCall('DELETE', `/camas/${camaId}`); } else { // Marcar como disponible si es un área normal await apiCall('PUT', `/camas/${camaId}`, { estado: 'Disponible', pacienteId: null, internacionId: null }); } } // Actualizar estado local después del éxito setState(prev => { return { ...prev, internaciones: prev.internaciones.map(i => i.id === internacionId ? { ...i, ...datos, activa: false } : i ), camas: debeEliminarCama ? prev.camas.filter(c => c.id !== camaId) : prev.camas.map(c => c.id === camaId ? { ...c, estado: 'Disponible' as const, pacienteId: undefined, internacionId: undefined } : c ), }; }); } catch (err) { console.error('Error al finalizar internación:', err); throw err; } }, [state, apiCall]); const eliminarInternacion = useCallback(async (internacionId: string) => { const internacion = state.internaciones.find(i => i.id === internacionId); if (!internacion) return; const grupoId = internacion.grupoId || null; if (!checkGrupoPermission(grupoId)) { console.warn('No tiene permisos para eliminar internación en esta área'); toast.error('No tiene permisos para eliminar internación en esta área'); return; } const grupoFueraDeGrupo = state.grupos.find(a => (a.nombre || '').toLowerCase().trim() === 'fuera de grupo'); const grupoFueraDeGrupoId = grupoFueraDeGrupo?.id; const esFueraDeGrupo = !internacion.grupoId || !grupoFueraDeGrupoId || internacion.grupoId === grupoFueraDeGrupoId; const camaId = internacion.camaId; const camaObj = camaId ? state.camas.find(c => c.id === camaId) : undefined; const debeEliminarCama = camaObj ? (computeSector(camaObj.numero) === 'Fuera de Área') : esFueraDeGrupo; try { await apiCall('DELETE', `/internaciones/${internacionId}`); if (camaId) { if (debeEliminarCama) { await apiCall('DELETE', `/camas/${camaId}`); } else { await apiCall('PUT', `/camas/${camaId}`, { estado: 'Disponible', pacienteId: null, internacionId: null }); } } setState(prev => ({ ...prev, internaciones: prev.internaciones.filter(i => i.id !== internacionId), camas: debeEliminarCama ? prev.camas.filter(c => c.id !== camaId) : prev.camas.map(c => c.id === camaId ? { ...c, estado: 'Disponible' as const, pacienteId: undefined, internacionId: undefined } : c ), evoluciones: prev.evoluciones.filter(e => e.internacionId !== internacionId), laboratorios: prev.laboratorios.filter(l => l.internacionId !== internacionId), glucemias: prev.glucemias.filter(g => g.internacionId !== internacionId), acidosBase: prev.acidosBase.filter(a => a.internacionId !== internacionId), cultivos: prev.cultivos.filter(c => c.internacionId !== internacionId), estudiosComplementarios: prev.estudiosComplementarios.filter(e => e.internacionId !== internacionId), interconsultas: (prev.interconsultas || []).filter(ic => ic.internacionId !== internacionId), atb: (prev.atb || []).filter(a => a.internacionId !== internacionId), indicaciones: (prev.indicaciones || []).filter(i => i.internacionId !== internacionId), })); toast.success('Historia clínica de internación eliminada correctamente'); } catch (err) { console.error('Error al eliminar internación:', err); toast.error('Error al eliminar internación'); throw err; } }, [state, checkGrupoPermission, apiCall]); const actualizarInternacion = useCallback(async (internacionId: string, datos: Partial) => { const internacion = state.internaciones.find(i => i.id === internacionId); if (!internacion) return; // Check permissions if (!canEditIngreso(internacionId)) { toast.error(`No tiene permisos para modificar este ingreso.`); return; } const oldCamaId = internacion.camaId; const newCamaId = datos.camaId; const cambioDeCama = newCamaId && newCamaId !== oldCamaId; if (cambioDeCama && !canChangeBed(internacionId)) { console.warn('No tiene permisos para cambiar la cama de esta internación'); checkGrupoPermission(null); // triggers toast return; } if (!cambioDeCama && !checkGrupoPermission(internacion.grupoId)) { console.warn('No tiene permisos para actualizar esta internación'); return; } try { // API call inmediata await apiCall('PUT', `/internaciones/${internacionId}`, datos); // Si hay cambio de cama, actualizar estados en la base de datos if (cambioDeCama && newCamaId && oldCamaId) { await apiCall('PUT', `/camas/${newCamaId}`, { estado: 'Ocupada' }); await apiCall('PUT', `/camas/${oldCamaId}`, { estado: 'Disponible' }); } // Actualizar estado local después del éxito setState(prev => { const result = { ...prev, internaciones: prev.internaciones.map(i => i.id === internacionId ? { ...i, ...datos } : i ), camas: prev.camas.map(c => { if (c.id === newCamaId && newCamaId !== oldCamaId) { return { ...c, estado: 'Ocupada' as const }; } if (c.id === oldCamaId && oldCamaId !== newCamaId) { return { ...c, estado: 'Disponible' as const }; } return c; }), }; return result; }); } catch (err) { console.error('Error al actualizar internación:', err); throw err; } }, [state, canChangeBed, canEditIngreso, checkGrupoPermission, apiCall]); // Acciones de evoluciones const agregarEvolucion = useCallback(async (evolucion: Omit) => { const internacion = state.internaciones.find(i => i.id === evolucion.internacionId); const grupoId = internacion?.grupoId || null; if (!checkGrupoPermission(grupoId)) { console.warn('No tiene permisos para agregar evolución en esta área'); return null; } const nuevaEvolucion: Evolucion = { ...evolucion, id: generateUUID(), }; try { await apiCall('POST', '/evoluciones', nuevaEvolucion); setState(prev => ({ ...prev, evoluciones: [...prev.evoluciones, nuevaEvolucion], })); return nuevaEvolucion.id; } catch (err) { console.error('Error al agregar evolución:', err); throw err; } }, [state, checkGrupoPermission, apiCall]); const eliminarEvolucion = useCallback(async (id: string) => { const evolucion = state.evoluciones.find(e => e.id === id); if (!evolucion) return; const internacion = state.internaciones.find(i => i.id === evolucion.internacionId); const grupoId = internacion?.grupoId || null; if (!checkGrupoPermission(grupoId)) { console.warn('No tiene permisos para eliminar evolución en esta área'); return; } try { await apiCall('DELETE', `/evoluciones/${id}`); setState(prev => ({ ...prev, evoluciones: prev.evoluciones.filter(e => e.id !== id), })); } catch (err) { console.error('Error al eliminar evolución:', err); throw err; } }, [state, checkGrupoPermission, apiCall]); const actualizarEvolucion = useCallback(async (id: string, datos: Partial) => { const evolucion = state.evoluciones.find(e => e.id === id); if (!evolucion) return; const internacion = state.internaciones.find(i => i.id === evolucion.internacionId); const grupoId = internacion?.grupoId || null; if (!checkGrupoPermission(grupoId)) { console.warn('No tiene permisos para actualizar evolución en esta área'); return; } try { await apiCall('PUT', `/evoluciones/${id}`, datos); setState(prev => ({ ...prev, evoluciones: prev.evoluciones.map(e => e.id === id ? { ...e, ...datos } : e), })); } catch (err) { console.error('Error al actualizar evolución:', err); throw err; } }, [state, checkGrupoPermission, apiCall]); // Acciones de laboratorios const agregarLaboratorio = useCallback(async (laboratorio: Omit) => { const internacion = state.internaciones.find(i => i.id === laboratorio.internacionId); const grupoId = internacion?.grupoId || null; if (!checkGrupoPermission(grupoId)) { console.warn('No tiene permisos para agregar laboratorio en esta área'); return null; } const nuevoLaboratorio: Laboratorio = { ...laboratorio, id: generateUUID(), }; try { await apiCall('POST', '/laboratorios', nuevoLaboratorio); setState(prev => ({ ...prev, laboratorios: [...prev.laboratorios, nuevoLaboratorio], })); return nuevoLaboratorio.id; } catch (err) { console.error('Error al agregar laboratorio:', err); throw err; } }, [state, checkGrupoPermission, apiCall]); const eliminarLaboratorio = useCallback(async (id: string) => { const laboratorio = state.laboratorios.find(l => l.id === id); if (!laboratorio) return; const internacion = state.internaciones.find(i => i.id === laboratorio.internacionId); const grupoId = internacion?.grupoId || null; if (!checkGrupoPermission(grupoId)) { console.warn('No tiene permisos para eliminar laboratorio en esta área'); return; } try { await apiCall('DELETE', `/laboratorios/${id}`); setState(prev => ({ ...prev, laboratorios: prev.laboratorios.filter(l => l.id !== id), })); } catch (err) { console.error('Error al eliminar laboratorio:', err); throw err; } }, [state, checkGrupoPermission, apiCall]); const actualizarLaboratorio = useCallback(async (id: string, datos: Partial) => { const laboratorio = state.laboratorios.find(l => l.id === id); if (!laboratorio) return; const internacion = state.internaciones.find(i => i.id === laboratorio.internacionId); const grupoId = internacion?.grupoId || null; if (!checkGrupoPermission(grupoId)) { console.warn('No tiene permisos para actualizar laboratorio en esta área'); return; } try { await apiCall('PUT', `/laboratorios/${id}`, datos); setState(prev => ({ ...prev, laboratorios: prev.laboratorios.map(l => l.id === id ? { ...l, ...datos } : l), })); } catch (err) { console.error('Error al actualizar laboratorio:', err); throw err; } }, [state, checkGrupoPermission, apiCall]); // Acciones de glucemias const agregarGlucemia = useCallback(async (glucemia: Omit) => { const internacion = state.internaciones.find(i => i.id === glucemia.internacionId); const grupoId = internacion?.grupoId || null; if (!checkGrupoPermission(grupoId)) { console.warn('No tiene permisos para agregar glucemia en esta área'); return null; } const nuevaGlucemia: Glucemia = { ...glucemia, id: generateUUID(), }; try { await apiCall('POST', '/glucemias', nuevaGlucemia); setState(prev => ({ ...prev, glucemias: [...prev.glucemias, nuevaGlucemia], })); return nuevaGlucemia.id; } catch (err) { console.error('Error al agregar glucemia:', err); throw err; } }, [state, checkGrupoPermission, apiCall]); const eliminarGlucemia = useCallback(async (id: string) => { const glucemia = state.glucemias.find(g => g.id === id); if (!glucemia) return; const internacion = state.internaciones.find(i => i.id === glucemia.internacionId); const grupoId = internacion?.grupoId || null; if (!checkGrupoPermission(grupoId)) { console.warn('No tiene permisos para eliminar glucemia en esta área'); return; } try { await apiCall('DELETE', `/glucemias/${id}`); setState(prev => ({ ...prev, glucemias: prev.glucemias.filter(g => g.id !== id), })); } catch (err) { console.error('Error al eliminar glucemia:', err); throw err; } }, [state, checkGrupoPermission, apiCall]); const actualizarGlucemia = useCallback(async (id: string, datos: Partial) => { const glucemia = state.glucemias.find(g => g.id === id); if (!glucemia) return; const internacion = state.internaciones.find(i => i.id === glucemia.internacionId); const grupoId = internacion?.grupoId || null; if (!checkGrupoPermission(grupoId)) { console.warn('No tiene permisos para actualizar glucemia en esta área'); return; } try { await apiCall('PUT', `/glucemias/${id}`, datos); setState(prev => ({ ...prev, glucemias: prev.glucemias.map(g => g.id === id ? { ...g, ...datos } : g), })); } catch (err) { console.error('Error al actualizar glucemia:', err); throw err; } }, [state, checkGrupoPermission, apiCall]); // Acciones de ácido-base const agregarAcidoBase = useCallback(async (acidoBase: Omit) => { const internacion = state.internaciones.find(i => i.id === acidoBase.internacionId); const grupoId = internacion?.grupoId || null; if (!checkGrupoPermission(grupoId)) { console.warn('No tiene permisos para agregar ácido-base en esta área'); return null; } const nuevoAcidoBase: AcidoBase = { ...acidoBase, id: generateUUID(), }; try { await apiCall('POST', '/acid-os-base', nuevoAcidoBase); setState(prev => ({ ...prev, acidosBase: [...prev.acidosBase, nuevoAcidoBase], })); return nuevoAcidoBase.id; } catch (err) { console.error('Error al agregar ácido-base:', err); throw err; } }, [state, checkGrupoPermission, apiCall]); const actualizarAcidoBase = useCallback(async (id: string, datos: Partial) => { const acidoBase = state.acidosBase.find(a => a.id === id); if (!acidoBase) return; const internacion = state.internaciones.find(i => i.id === acidoBase.internacionId); const grupoId = internacion?.grupoId || null; if (!checkGrupoPermission(grupoId)) { console.warn('No tiene permisos para actualizar ácido-base en esta área'); return; } try { await apiCall('PUT', `/acid-os-base/${id}`, datos); setState(prev => ({ ...prev, acidosBase: prev.acidosBase.map(a => a.id === id ? { ...a, ...datos } : a), })); } catch (err) { console.error('Error al actualizar ácido-base:', err); throw err; } }, [state, checkGrupoPermission, apiCall]); const eliminarAcidoBase = useCallback(async (id: string) => { const acidoBase = state.acidosBase.find(a => a.id === id); if (!acidoBase) return; const internacion = state.internaciones.find(i => i.id === acidoBase.internacionId); const grupoId = internacion?.grupoId || null; if (!checkGrupoPermission(grupoId)) { console.warn('No tiene permisos para eliminar ácido-base en esta área'); return; } try { await apiCall('DELETE', `/acid-os-base/${id}`); setState(prev => ({ ...prev, acidosBase: prev.acidosBase.filter(a => a.id !== id), })); } catch (err) { console.error('Error al eliminar ácido-base:', err); throw err; } }, [state, checkGrupoPermission, apiCall]); // Acciones de cultivos const agregarCultivo = useCallback(async (cultivo: Omit) => { const internacion = state.internaciones.find(i => i.id === cultivo.internacionId); const grupoId = internacion?.grupoId || null; if (!checkGrupoPermission(grupoId)) { console.warn('No tiene permisos para agregar cultivo en esta área'); return null; } const nuevoCultivo: Cultivo = { ...cultivo, id: generateUUID(), }; try { await apiCall('POST', '/cultivos', nuevoCultivo); setState(prev => ({ ...prev, cultivos: [...prev.cultivos, nuevoCultivo], })); return nuevoCultivo.id; } catch (err) { console.error('Error al agregar cultivo:', err); throw err; } }, [state, checkGrupoPermission, apiCall]); const actualizarCultivo = useCallback(async (id: string, datos: Partial) => { const cultivo = state.cultivos.find(c => c.id === id); if (!cultivo) return; const internacion = state.internaciones.find(i => i.id === cultivo.internacionId); const grupoId = internacion?.grupoId || null; if (!checkGrupoPermission(grupoId)) { console.warn('No tiene permisos para actualizar cultivo en esta área'); return; } try { await apiCall('PUT', `/cultivos/${id}`, datos); setState(prev => ({ ...prev, cultivos: prev.cultivos.map(c => c.id === id ? { ...c, ...datos } : c), })); } catch (err) { console.error('Error al actualizar cultivo:', err); throw err; } }, [state, checkGrupoPermission, apiCall]); const eliminarCultivo = useCallback(async (id: string) => { const cultivo = state.cultivos.find(c => c.id === id); if (!cultivo) return; const internacion = state.internaciones.find(i => i.id === cultivo.internacionId); const grupoId = internacion?.grupoId || null; if (!checkGrupoPermission(grupoId)) { console.warn('No tiene permisos para eliminar cultivo en esta área'); return; } try { await apiCall('DELETE', `/cultivos/${id}`); setState(prev => ({ ...prev, cultivos: prev.cultivos.filter(c => c.id !== id), })); } catch (err) { console.error('Error al eliminar cultivo:', err); throw err; } }, [state, checkGrupoPermission, apiCall]); // Acciones de estudios complementarios const agregarEstudioComplementario = useCallback(async (estudio: Omit) => { const internacion = state.internaciones.find(i => i.id === estudio.internacionId); const grupoId = internacion?.grupoId || null; if (!checkGrupoPermission(grupoId)) { console.warn('No tiene permisos para agregar estudio complementario en esta área'); return null; } const nuevoEstudio: EstudioComplementario = { ...estudio, id: generateUUID(), }; try { await apiCall('POST', '/estudios-complementarios', nuevoEstudio); setState(prev => ({ ...prev, estudiosComplementarios: [...prev.estudiosComplementarios, nuevoEstudio], })); return nuevoEstudio.id; } catch (err) { console.error('Error al agregar estudio:', err); throw err; } }, [state, checkGrupoPermission, apiCall]); const actualizarEstudioComplementario = useCallback(async (id: string, datos: Partial) => { const estudio = state.estudiosComplementarios.find(e => e.id === id); if (!estudio) return; const internacion = state.internaciones.find(i => i.id === estudio.internacionId); const grupoId = internacion?.grupoId || null; if (!checkGrupoPermission(grupoId)) { console.warn('No tiene permisos para actualizar estudio complementario en esta área'); return; } try { await apiCall('PUT', `/estudios-complementarios/${id}`, datos); setState(prev => ({ ...prev, estudiosComplementarios: prev.estudiosComplementarios.map(e => e.id === id ? { ...e, ...datos } : e), })); } catch (err) { console.error('Error al actualizar estudio:', err); throw err; } }, [state, checkGrupoPermission, apiCall]); const eliminarEstudioComplementario = useCallback(async (id: string) => { const estudio = state.estudiosComplementarios.find(e => e.id === id); if (!estudio) return; const internacion = state.internaciones.find(i => i.id === estudio.internacionId); const grupoId = internacion?.grupoId || null; if (!checkGrupoPermission(grupoId)) { console.warn('No tiene permisos para eliminar estudio complementario en esta área'); return; } try { await apiCall('DELETE', `/estudios-complementarios/${id}`); setState(prev => ({ ...prev, estudiosComplementarios: prev.estudiosComplementarios.filter(e => e.id !== id), })); } catch (err) { console.error('Error al eliminar estudio:', err); throw err; } }, [state, checkGrupoPermission, apiCall]); // Acciones de interconsultas const agregarInterconsulta = useCallback(async (interconsulta: Omit) => { const internacion = state.internaciones.find(i => i.id === interconsulta.internacionId); const grupoId = internacion?.grupoId || null; if (!checkGrupoPermission(grupoId)) { console.warn('No tiene permisos para agregar interconsulta en esta área'); return null; } const nuevaInterconsulta: Interconsulta = { ...interconsulta, id: generateUUID() }; try { await apiCall('POST', '/interconsultas', nuevaInterconsulta); setState(prev => ({ ...prev, interconsultas: [...(prev.interconsultas || []), nuevaInterconsulta], })); return nuevaInterconsulta.id; } catch (err) { console.error('Error al agregar interconsulta:', err); throw err; } }, [state, checkGrupoPermission, apiCall]); const actualizarInterconsulta = useCallback(async (id: string, datos: Partial) => { const interconsulta = (state.interconsultas || []).find(ic => ic.id === id); if (!interconsulta) return; const internacion = state.internaciones.find(i => i.id === interconsulta.internacionId); const grupoId = internacion?.grupoId || null; if (!checkGrupoPermission(grupoId)) { console.warn('No tiene permisos para actualizar interconsulta en esta área'); return; } try { await apiCall('PUT', `/interconsultas/${id}`, datos); setState(prev => ({ ...prev, interconsultas: (prev.interconsultas || []).map(ic => ic.id === id ? { ...ic, ...datos } : ic), })); } catch (err) { console.error('Error al actualizar interconsulta:', err); throw err; } }, [state, checkGrupoPermission, apiCall]); const eliminarInterconsulta = useCallback(async (id: string) => { const interconsulta = (state.interconsultas || []).find(ic => ic.id === id); if (!interconsulta) return; const internacion = state.internaciones.find(i => i.id === interconsulta.internacionId); const grupoId = internacion?.grupoId || null; if (!checkGrupoPermission(grupoId)) { console.warn('No tiene permisos para eliminar interconsulta en esta área'); return; } try { await apiCall('DELETE', `/interconsultas/${id}`); setState(prev => ({ ...prev, interconsultas: (prev.interconsultas || []).filter(ic => ic.id !== id), })); } catch (err) { console.error('Error al eliminar interconsulta:', err); throw err; } }, [state, checkGrupoPermission, apiCall]); const agregarATB = useCallback(async (atb: Omit) => { const internacion = state.internaciones.find(i => i.id === atb.internacionId); const grupoId = internacion?.grupoId || null; if (!checkGrupoPermission(grupoId)) { console.warn('No tiene permisos para agregar ATB en esta área'); return null; } const nuevoATB: ATB = { ...atb, id: generateUUID() }; try { await apiCall('POST', '/atb', nuevoATB); setState(prev => ({ ...prev, atb: [...prev.atb, nuevoATB], })); return nuevoATB.id; } catch (err) { console.error('Error al agregar ATB:', err); throw err; } }, [state, checkGrupoPermission, apiCall]); const actualizarATB = useCallback(async (id: string, datos: Partial) => { const atb = state.atb.find(a => a.id === id); if (!atb) return; const internacion = state.internaciones.find(i => i.id === atb.internacionId); const grupoId = internacion?.grupoId || null; if (!checkGrupoPermission(grupoId)) { console.warn('No tiene permisos para actualizar ATB en esta área'); return; } try { await apiCall('PUT', `/atb/${id}`, datos); setState(prev => ({ ...prev, atb: prev.atb.map(a => a.id === id ? { ...a, ...datos } : a), })); } catch (err) { console.error('Error al actualizar ATB:', err); throw err; } }, [state, checkGrupoPermission, apiCall]); const eliminarATB = useCallback(async (id: string) => { const atb = state.atb.find(a => a.id === id); if (!atb) return; const internacion = state.internaciones.find(i => i.id === atb.internacionId); const grupoId = internacion?.grupoId || null; if (!checkGrupoPermission(grupoId)) { console.warn('No tiene permisos para eliminar ATB en esta área'); return; } try { await apiCall('DELETE', `/atb/${id}`); setState(prev => ({ ...prev, atb: prev.atb.filter(a => a.id !== id), })); } catch (err) { console.error('Error al eliminar ATB:', err); throw err; } }, [state, checkGrupoPermission, apiCall]); const agregarIndicacion = useCallback(async (indicacion: Omit) => { const internacion = state.internaciones.find(i => i.id === indicacion.internacionId); const grupoId = internacion?.grupoId || null; if (!checkGrupoPermission(grupoId)) { console.warn('No tiene permisos para agregar indicación en esta área'); return null; } const nuevaIndicacion: Indicacion = { ...indicacion, id: generateUUID() }; try { await apiCall('POST', '/indicaciones', nuevaIndicacion); setState(prev => ({ ...prev, indicaciones: [...prev.indicaciones, nuevaIndicacion], })); return nuevaIndicacion.id; } catch (err) { console.error('Error al agregar indicación:', err); throw err; } }, [state.internaciones, checkGrupoPermission, apiCall]); const actualizarIndicacion = useCallback(async (id: string, datos: Partial) => { const indicacion = state.indicaciones.find(i => i.id === id); if (!indicacion) return; const internacion = state.internaciones.find(i => i.id === indicacion.internacionId); const grupoId = internacion?.grupoId || null; if (!checkGrupoPermission(grupoId)) { console.warn('No tiene permisos para actualizar indicación en esta área'); return; } try { await apiCall('PUT', `/indicaciones/${id}`, datos); setState(prev => ({ ...prev, indicaciones: prev.indicaciones.map(i => i.id === id ? { ...i, ...datos } : i), })); } catch (err) { console.error('Error al actualizar indicación:', err); throw err; } }, [state.indicaciones, state.internaciones, checkGrupoPermission, apiCall]); const eliminarIndicacion = useCallback(async (id: string) => { const indicacion = state.indicaciones.find(i => i.id === id); if (!indicacion) return; const internacion = state.internaciones.find(i => i.id === indicacion.internacionId); const grupoId = internacion?.grupoId || null; if (!checkGrupoPermission(grupoId)) { console.warn('No tiene permisos para eliminar indicación en esta área'); return; } try { await apiCall('DELETE', `/indicaciones/${id}`); setState(prev => ({ ...prev, indicaciones: prev.indicaciones.filter(i => i.id !== id), })); } catch (err) { console.error('Error al eliminar indicación:', err); throw err; } }, [state.indicaciones, state.internaciones, checkGrupoPermission, apiCall]); const agregarMovimientoIndicacion = useCallback(async (movimiento: MovimientoIndicacion) => { const internacion = state.internaciones.find(i => i.id === movimiento.internacionId); const grupoId = internacion?.grupoId || null; if (!checkGrupoPermission(grupoId)) { console.warn('No tiene permisos para agregar movimiento de indicación en esta área'); return; } const nuevoMovimiento = { ...movimiento, id: generateUUID() }; try { await apiCall('POST', '/movimientos-indicaciones', nuevoMovimiento); setState(prev => ({ ...prev, movimientosIndicaciones: [...(prev.movimientosIndicaciones || []), nuevoMovimiento], })); } catch (err) { console.error('Error al agregar movimiento:', err); throw err; } }, [state, checkGrupoPermission, apiCall]); const getMovimientosByInternacion = useCallback((internacionId: string) => { return (state.movimientosIndicaciones || []) .filter((m: MovimientoIndicacion) => m.internacionId === internacionId) .sort((a: MovimientoIndicacion, b: MovimientoIndicacion) => new Date(b.fecha).getTime() - new Date(a.fecha).getTime()); }, [state.movimientosIndicaciones]); // Acciones de grupos const agregarGrupo = useCallback(async (grupo: Omit) => { if (state.currentUser?.rol !== 'admin') { toast.error('Solo el usuario administrador puede agregar grupos de trabajo'); return; } const nuevaGrupo: Grupo = { ...grupo, id: generateUUID() }; try { await apiCall('POST', '/grupos', nuevaGrupo); setState(prev => ({ ...prev, grupos: [...prev.grupos, nuevaGrupo], })); return nuevaGrupo.id; } catch (err) { console.error('Error al agregar área:', err); throw err; } }, [state.currentUser, apiCall]); const actualizarGrupo = useCallback(async (id: string, datos: Partial) => { if (state.currentUser?.rol !== 'admin') { toast.error('Solo el usuario administrador puede modificar grupos de trabajo'); return; } try { await apiCall('PUT', `/grupos/${id}`, datos); setState(prev => ({ ...prev, grupos: prev.grupos.map(a => a.id === id ? { ...a, ...datos } : a), })); } catch (err) { console.error('Error al actualizar área:', err); throw err; } }, [state.currentUser, apiCall]); const eliminarGrupo = useCallback(async (id: string) => { if (state.currentUser?.rol !== 'admin') { toast.error('Solo el usuario administrador puede eliminar grupos de trabajo'); return; } try { await apiCall('DELETE', `/grupos/${id}`); setState(prev => ({ ...prev, grupos: prev.grupos.filter(a => a.id !== id), camas: prev.camas.map(c => c.grupoId === id ? { ...c, grupoId: undefined } : c), })); } catch (err) { console.error('Error al eliminar área:', err); throw err; } }, [state.currentUser, apiCall]); // Funciones de utilidad para consultas const getPacienteById = useCallback((id: string) => { if (!id) return undefined; const target = String(id).trim().toLowerCase(); return state.pacientes.find(p => (p.id && String(p.id).trim().toLowerCase() === target) || (p.dni && String(p.dni).trim().toLowerCase() === target) ); }, [state.pacientes]); const getCamaById = useCallback((id: string) => { if (!id) return undefined; const target = String(id).trim().toLowerCase(); return state.camas.find(c => c.id && String(c.id).trim().toLowerCase() === target); }, [state.camas]); const getInternacionById = useCallback((id: string) => { if (!id) return undefined; const target = String(id).trim().toLowerCase(); return state.internaciones.find(i => i.id && String(i.id).trim().toLowerCase() === target); }, [state.internaciones]); const getInternacionActivaByPaciente = useCallback((pacienteId: string) => { return state.internaciones.find(i => i.pacienteId === pacienteId && i.activa); }, [state.internaciones]); const getEvolucionesByInternacion = useCallback((internacionId: string) => { return state.evoluciones .filter(e => e.internacionId === internacionId) .sort((a, b) => new Date(b.fecha + 'T' + b.hora).getTime() - new Date(a.fecha + 'T' + a.hora).getTime()); }, [state.evoluciones]); const getLaboratoriosByInternacion = useCallback((internacionId: string) => { return state.laboratorios .filter(l => l.internacionId === internacionId) .sort((a, b) => new Date(b.fecha).getTime() - new Date(a.fecha).getTime()); }, [state.laboratorios]); const getGlucemiasByInternacion = useCallback((internacionId: string) => { return state.glucemias .filter(g => g.internacionId === internacionId) .sort((a, b) => new Date(b.fecha + 'T' + (b.hora || '00:00')).getTime() - new Date(a.fecha + 'T' + (a.hora || '00:00')).getTime()); }, [state.glucemias]); const getAcidosBaseByInternacion = useCallback((internacionId: string) => { return state.acidosBase .filter(a => a.internacionId === internacionId) .sort((a, b) => new Date(b.fecha + 'T' + b.hora).getTime() - new Date(a.fecha + 'T' + a.hora).getTime()); }, [state.acidosBase]); const getCultivosByInternacion = useCallback((internacionId: string) => { return state.cultivos .filter(c => c.internacionId === internacionId) .sort((a, b) => new Date(b.fechaToma).getTime() - new Date(a.fechaToma).getTime()); }, [state.cultivos]); const agregarPendiente = useCallback(async (datos: Omit) => { const nuevoPendiente: Pendiente = { ...datos, id: generateUUID(), fechaCreacion: datos.fechaCreacion || new Date().toISOString().split('T')[0], horaCreacion: datos.horaCreacion || new Date().toTimeString().slice(0, 5), estado: datos.estado || 'pendiente', prioridad: datos.prioridad || 'media', }; try { await apiCall('POST', '/pendientes', nuevoPendiente); setState(prev => ({ ...prev, pendientes: [...(prev.pendientes || []), nuevoPendiente], })); return nuevoPendiente.id; } catch (err) { console.error('Error al agregar pendiente:', err); throw err; } }, [apiCall]); const actualizarPendiente = useCallback(async (id: string, datos: Partial) => { try { await apiCall('PUT', `/pendientes/${id}`, datos); setState(prev => ({ ...prev, pendientes: (prev.pendientes || []).map(p => p.id === id ? { ...p, ...datos } : p), })); } catch (err) { console.error('Error al actualizar pendiente:', err); throw err; } }, [apiCall]); const eliminarPendiente = useCallback(async (id: string) => { try { await apiCall('DELETE', `/pendientes/${id}`); setState(prev => ({ ...prev, pendientes: (prev.pendientes || []).filter(p => p.id !== id), })); } catch (err) { console.error('Error al eliminar pendiente:', err); throw err; } }, [apiCall]); const getPendientesByPaciente = useCallback((pacienteId: string) => { return (state.pendientes || []) .filter(p => p.pacienteId === pacienteId) .sort((a, b) => new Date(`${b.fechaCreacion}T${b.horaCreacion || '00:00'}`).getTime() - new Date(`${a.fechaCreacion}T${a.horaCreacion || '00:00'}`).getTime()); }, [state.pendientes]); const getEstadisticas = useCallback(() => { const camasEnArea = state.camas.filter(c => !isCamaFueraDeGrupo(c)); const camasFueraDeArea = state.camas.filter(c => isCamaFueraDeGrupo(c)); const camasOcupadas = camasEnArea.filter(c => c.estado === 'Ocupada').length; const camasDisponibles = camasEnArea.filter(c => c.estado === 'Disponible').length; const camasMantenimiento = state.camas.filter(c => c.estado === 'Reparacion').length; const internacionesActivas = state.internaciones.filter(i => i.activa).length; const totalPacientes = state.pacientes.length; const cultivosPendientes = state.cultivos.filter(c => c.estado === 'NAF/Pendiente').length; return { camasOcupadas, camasDisponibles, camasMantenimiento, totalCamas: state.camas.length, totalCamasActivas: camasEnArea.length, camasFueraDeArea: camasFueraDeArea.length, internacionesActivas, totalPacientes, cultivosPendientes, porcentajeOcupacion: camasEnArea.length > 0 ? Math.round((camasOcupadas / camasEnArea.length) * 100) : 0, }; }, [state]); // Current internacion selection (for HC page) const setCurrentInternacion = useCallback((id?: string | null) => { if (id) { sessionStorage.setItem('hospital_current_internacion_id', id); } else { sessionStorage.removeItem('hospital_current_internacion_id'); } setState(prev => ({ ...prev, currentInternacionId: id ?? null })); }, []); // Auth functions const login = useCallback(async (dni: string, password: string) => { 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(); sessionStorage.setItem('hospital_user', JSON.stringify(user)); setState(prev => ({ ...prev, currentUser: user, isAuthenticated: true })); return user; }, []); const logout = useCallback(() => { sessionStorage.removeItem('hospital_user'); setState(prev => { const newState = { ...prev, currentUser: null, isAuthenticated: false }; 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 refreshState = useCallback(async () => { try { const res = await fetch(`${API_BASE}/state`); if (!res.ok) throw new Error('no state'); const body = await res.json(); if (body) { setState(prev => ({ ...prev, ...body, estudiosComplementarios: body.estudiosComplementarios || prev.estudiosComplementarios, interconsultas: body.interconsultas || prev.interconsultas, atb: body.atb || prev.atb, glucemias: body.glucemias || prev.glucemias, movimientosIndicaciones: body.movimientosIndicaciones || prev.movimientosIndicaciones, pendientes: body.pendientes || prev.pendientes, })); } } catch (err) { console.error('Error refreshing state:', err); } }, []); return { ...state, isLoaded, refreshState, setVista, agregarPaciente, actualizarPaciente, eliminarPaciente, actualizarCama, iniciarInternacion, finalizarInternacion, actualizarInternacion, eliminarInternacion, agregarCama, eliminarCama, agregarEvolucion, actualizarEvolucion, eliminarEvolucion, agregarLaboratorio, actualizarLaboratorio, eliminarLaboratorio, agregarGlucemia, actualizarGlucemia, eliminarGlucemia, agregarAcidoBase, actualizarAcidoBase, eliminarAcidoBase, agregarCultivo, actualizarCultivo, eliminarCultivo, agregarEstudioComplementario, actualizarEstudioComplementario, eliminarEstudioComplementario, agregarInterconsulta, actualizarInterconsulta, eliminarInterconsulta, agregarATB, actualizarATB, eliminarATB, agregarIndicacion, actualizarIndicacion, eliminarIndicacion, agregarMovimientoIndicacion, getMovimientosByInternacion, agregarGrupo, actualizarGrupo, eliminarGrupo, getPacienteById, getCamaById, getInternacionById, getInternacionActivaByPaciente, getEvolucionesByInternacion, getLaboratoriosByInternacion, getGlucemiasByInternacion, getAcidosBaseByInternacion, getCultivosByInternacion, agregarPendiente, actualizarPendiente, eliminarPendiente, getPendientesByPaciente, setCurrentInternacion, getEstadisticas, login, logout, changePassword, updateEmail, hasPermission, canAccessGrupo, canChangeBed, canEditInternacion, canEditIngreso, canEditCama, canEditPaciente, getCamaGrupoId, getInternacionGrupoId, getPacienteGrupoId, isLoaded, }; }