feat: update hospital management system and fix linter issues

This commit is contained in:
2026-08-10 01:33:05 +00:00
parent de3627b5d4
commit 735ccb9920
24 changed files with 309 additions and 271 deletions
+10
View File
@@ -0,0 +1,10 @@
const fs = require('fs');
let code = fs.readFileSync('src/App.tsx', 'utf8');
code = code.replace(/store\.getLaboratoriosByPaciente\(paciente\.id\)/g, 'store.getLaboratoriosByInternacion(internacion.id)');
code = code.replace(/store\.getGlucemiasByPaciente\(paciente\.id\)/g, 'store.getGlucemiasByInternacion(internacion.id)');
code = code.replace(/store\.getAcidosBaseByPaciente\(paciente\.id\)/g, 'store.getAcidosBaseByInternacion(internacion.id)');
code = code.replace(/store\.getCultivosByPaciente\(paciente\.id\)/g, 'store.getCultivosByInternacion(internacion.id)');
code = code.replace(/store\.estudiosComplementarios\.filter\(e => e\.pacienteId === paciente\.id\)/g, 'store.estudiosComplementarios.filter(e => e.internacionId === internacion.id)');
fs.writeFileSync('src/App.tsx', code);
+14 -23
View File
@@ -1,30 +1,21 @@
const fs = require('fs'); const fs = require('fs');
let code = fs.readFileSync('src/hooks/useHospitalStore.ts', 'utf8'); let code = fs.readFileSync('src/hooks/useHospitalStore.ts', 'utf8');
code = code.replace( code = code.replace(/getLaboratoriosByPaciente = useCallback\(\(pacienteId: string\)/g, 'getLaboratoriosByInternacion = useCallback((internacionId: string)');
/const nuevaCama: Cama = \{\s*\.\.\.cama,\s*id: generateUUID\(\),\s*\} as Cama;/, code = code.replace(/\.filter\(l => l\.pacienteId === pacienteId\)/g, '.filter(l => l.internacionId === internacionId)');
`const nuevaCama: Cama = {
...cama,
id: generateUUID(),
sector: computeSector(cama.numero),
} as Cama;`
);
// We need to import computeSector if it's not imported. code = code.replace(/getGlucemiasByPaciente = useCallback\(\(pacienteId: string\)/g, 'getGlucemiasByInternacion = useCallback((internacionId: string)');
// It is in utils.ts, and we already import isCamaFueraDeGrupo. code = code.replace(/\.filter\(g => g\.pacienteId === pacienteId\)/g, '.filter(g => g.internacionId === internacionId)');
code = code.replace(
/import \{ getNombreProfesional, isCamaFueraDeGrupo \} from '@\/lib\/utils';/,
`import { getNombreProfesional, isCamaFueraDeGrupo, computeSector } from '@/lib/utils';`
);
code = code.replace( code = code.replace(/getAcidosBaseByPaciente = useCallback\(\(pacienteId: string\)/g, 'getAcidosBaseByInternacion = useCallback((internacionId: string)');
/const actualizarCama = useCallback\(async \(id: string, datos: Partial<Cama>\) => \{\s*try \{\s*await apiCall\('PUT', `\/camas\/\$\{id\}`/, code = code.replace(/\.filter\(a => a\.pacienteId === pacienteId\)/g, '.filter(a => a.internacionId === internacionId)');
`const actualizarCama = useCallback(async (id: string, datos: Partial<Cama>) => {
try { code = code.replace(/getCultivosByPaciente = useCallback\(\(pacienteId: string\)/g, 'getCultivosByInternacion = useCallback((internacionId: string)');
if (datos.numero !== undefined) { code = code.replace(/\.filter\(c => c\.pacienteId === pacienteId\)/g, '.filter(c => c.internacionId === internacionId)');
datos.sector = computeSector(datos.numero);
} code = code.replace(/getLaboratoriosByPaciente,/g, 'getLaboratoriosByInternacion,');
await apiCall('PUT', \`/camas/\${id}\`` code = code.replace(/getGlucemiasByPaciente,/g, 'getGlucemiasByInternacion,');
); code = code.replace(/getAcidosBaseByPaciente,/g, 'getAcidosBaseByInternacion,');
code = code.replace(/getCultivosByPaciente,/g, 'getCultivosByInternacion,');
fs.writeFileSync('src/hooks/useHospitalStore.ts', code); fs.writeFileSync('src/hooks/useHospitalStore.ts', code);
+1 -1
View File
@@ -458,7 +458,7 @@ export function updateCama(id, updates) {
if (fields.length > 0) { if (fields.length > 0) {
values.push(id); values.push(id);
db.prepare(`UPDATE camas SET ${fields.join(', ')} WHERE id = ?`).run(values); db.prepare(`UPDATE camas SET ${fields.join(', ')} WHERE id = ?`).run(...values);
} }
} }
+11 -9
View File
@@ -17,6 +17,7 @@ import { Spinner } from '@/components/ui/spinner';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import { ThemeProvider } from "@/components/theme-provider"; import { ThemeProvider } from "@/components/theme-provider";
import { Toaster } from '@/components/ui/sonner'; import { Toaster } from '@/components/ui/sonner';
import type { Indicacion, MovimientoIndicacion } from '@/types';
function AppContent() { function AppContent() {
const store = useHospitalStore(); const store = useHospitalStore();
@@ -53,7 +54,7 @@ function AppContent() {
pacientes={store.pacientes} pacientes={store.pacientes}
internaciones={store.internaciones} internaciones={store.internaciones}
onActualizarCama={store.actualizarCama} onActualizarCama={store.actualizarCama}
onAgregarCama={store.agregarCama as any} onAgregarCama={store.agregarCama}
onEliminarCama={store.eliminarCama} onEliminarCama={store.eliminarCama}
onIniciarInternacion={store.iniciarInternacion} onIniciarInternacion={store.iniciarInternacion}
getPacienteById={store.getPacienteById} getPacienteById={store.getPacienteById}
@@ -85,6 +86,7 @@ function AppContent() {
cultivos={store.cultivos} cultivos={store.cultivos}
onIniciarInternacion={store.iniciarInternacion} onIniciarInternacion={store.iniciarInternacion}
onFinalizarInternacion={store.finalizarInternacion} onFinalizarInternacion={store.finalizarInternacion}
onEliminarInternacion={store.eliminarInternacion}
getPacienteById={store.getPacienteById} getPacienteById={store.getPacienteById}
getCamaById={store.getCamaById} getCamaById={store.getCamaById}
onVerHC={(id) => { store.setCurrentInternacion(id); store.setVista('historiaclinica'); }} onVerHC={(id) => { store.setCurrentInternacion(id); store.setVista('historiaclinica'); }}
@@ -134,7 +136,6 @@ function AppContent() {
case 'historiaclinica': { case 'historiaclinica': {
const internacionId = store.currentInternacionId || ''; const internacionId = store.currentInternacionId || '';
const internacion = store.getInternacionById(internacionId); const internacion = store.getInternacionById(internacionId);
const { datosVitales, evoluciones, laboratorios, acidosBase, cultivos, estudios, interconsultas, atb, indicaciones } = store;
const paciente = internacion ? store.getPacienteById(internacion.pacienteId) : undefined; const paciente = internacion ? store.getPacienteById(internacion.pacienteId) : undefined;
if (!internacion || !paciente) { if (!internacion || !paciente) {
return ( return (
@@ -151,11 +152,11 @@ function AppContent() {
cama={store.getCamaById(internacion.camaId)} cama={store.getCamaById(internacion.camaId)}
allCamas={store.camas} allCamas={store.camas}
evoluciones={store.getEvolucionesByInternacion(internacion.id)} evoluciones={store.getEvolucionesByInternacion(internacion.id)}
laboratorios={store.getLaboratoriosByPaciente(paciente.id)} laboratorios={store.getLaboratoriosByInternacion(internacion.id)}
glucemias={store.getGlucemiasByPaciente(paciente.id)} glucemias={store.getGlucemiasByInternacion(internacion.id)}
acidosBase={store.getAcidosBaseByPaciente(paciente.id)} acidosBase={store.getAcidosBaseByInternacion(internacion.id)}
cultivos={store.getCultivosByPaciente(paciente.id)} cultivos={store.getCultivosByInternacion(internacion.id)}
estudiosComplementarios={store.estudiosComplementarios.filter(e => e.pacienteId === paciente.id)} estudiosComplementarios={store.estudiosComplementarios.filter(e => e.internacionId === internacion.id)}
interconsultas={(store.interconsultas || []).filter(ic => ic.internacionId === internacion.id)} interconsultas={(store.interconsultas || []).filter(ic => ic.internacionId === internacion.id)}
atb={(store.atb || []).filter(a => a.internacionId === internacion.id)} atb={(store.atb || []).filter(a => a.internacionId === internacion.id)}
onAgregarEvolucion={store.agregarEvolucion} onAgregarEvolucion={store.agregarEvolucion}
@@ -182,12 +183,12 @@ function AppContent() {
onAgregarATB={(atb) => store.agregarATB({ ...atb, internacionId: internacion.id })} onAgregarATB={(atb) => store.agregarATB({ ...atb, internacionId: internacion.id })}
onActualizarATB={store.actualizarATB} onActualizarATB={store.actualizarATB}
onEliminarATB={store.eliminarATB} onEliminarATB={store.eliminarATB}
indicadores={(Array.isArray(store.indicaciones) ? store.indicaciones : []).filter((i: any) => i.internacionId === internacion.id)} indicadores={(Array.isArray(store.indicaciones) ? store.indicaciones : []).filter((i: Indicacion) => i.internacionId === internacion.id)}
movimientos={store.getMovimientosByInternacion ? store.getMovimientosByInternacion(internacion.id) : []} movimientos={store.getMovimientosByInternacion ? store.getMovimientosByInternacion(internacion.id) : []}
onAgregarIndicacion={(i) => store.agregarIndicacion({ ...i, internacionId: internacion.id })} onAgregarIndicacion={(i) => store.agregarIndicacion({ ...i, internacionId: internacion.id })}
onActualizarIndicacion={store.actualizarIndicacion} onActualizarIndicacion={store.actualizarIndicacion}
onEliminarIndicacion={store.eliminarIndicacion} onEliminarIndicacion={store.eliminarIndicacion}
onAgregarMovimiento={(m: any) => store.agregarMovimientoIndicacion ? store.agregarMovimientoIndicacion({ ...m, internacionId: internacion.id }) : null} onAgregarMovimiento={(m: MovimientoIndicacion) => store.agregarMovimientoIndicacion ? store.agregarMovimientoIndicacion({ ...m, internacionId: internacion.id }) : null}
onActualizarInternacion={store.actualizarInternacion} onActualizarInternacion={store.actualizarInternacion}
onActualizarCama={store.actualizarCama} onActualizarCama={store.actualizarCama}
onVolver={() => store.setVista('internaciones')} onVolver={() => store.setVista('internaciones')}
@@ -212,6 +213,7 @@ function AppContent() {
onAgregarCama={store.agregarCama} onAgregarCama={store.agregarCama}
onVolver={() => store.setVista('internaciones')} onVolver={() => store.setVista('internaciones')}
getGrupoName={(grupoId) => store.grupos.find(a => a.id === grupoId)?.nombre || ''} getGrupoName={(grupoId) => store.grupos.find(a => a.id === grupoId)?.nombre || ''}
internaciones={store.internaciones}
/> />
); );
+1
View File
@@ -1,3 +1,4 @@
/* eslint-disable react-refresh/only-export-components */
import { createContext, useContext, useEffect, useState } from "react" import { createContext, useContext, useEffect, useState } from "react"
type Theme = "dark" | "light" | "system" type Theme = "dark" | "light" | "system"
+1
View File
@@ -1,3 +1,4 @@
/* eslint-disable react-refresh/only-export-components */
import * as React from "react" import * as React from "react"
import { Slot } from "@radix-ui/react-slot" import { Slot } from "@radix-ui/react-slot"
import { cva, type VariantProps } from "class-variance-authority" import { cva, type VariantProps } from "class-variance-authority"
+1
View File
@@ -1,3 +1,4 @@
/* eslint-disable react-refresh/only-export-components */
import { Slot } from "@radix-ui/react-slot" import { Slot } from "@radix-ui/react-slot"
import { cva, type VariantProps } from "class-variance-authority" import { cva, type VariantProps } from "class-variance-authority"
+1
View File
@@ -1,3 +1,4 @@
/* eslint-disable react-refresh/only-export-components */
import * as React from "react" import * as React from "react"
import { Slot } from "@radix-ui/react-slot" import { Slot } from "@radix-ui/react-slot"
import { cva, type VariantProps } from "class-variance-authority" import { cva, type VariantProps } from "class-variance-authority"
+1
View File
@@ -1,5 +1,6 @@
"use client" "use client"
/* eslint-disable react-refresh/only-export-components */
import * as React from "react" import * as React from "react"
import type * as LabelPrimitive from "@radix-ui/react-label" import type * as LabelPrimitive from "@radix-ui/react-label"
import { Slot } from "@radix-ui/react-slot" import { Slot } from "@radix-ui/react-slot"
+1
View File
@@ -1,3 +1,4 @@
/* eslint-disable react-refresh/only-export-components */
import * as React from "react" import * as React from "react"
import * as NavigationMenuPrimitive from "@radix-ui/react-navigation-menu" import * as NavigationMenuPrimitive from "@radix-ui/react-navigation-menu"
import { cva } from "class-variance-authority" import { cva } from "class-variance-authority"
+2 -3
View File
@@ -1,5 +1,6 @@
"use client" "use client"
/* eslint-disable react-refresh/only-export-components */
import * as React from "react" import * as React from "react"
import { Slot } from "@radix-ui/react-slot" import { Slot } from "@radix-ui/react-slot"
import { cva, type VariantProps } from "class-variance-authority" import { cva, type VariantProps } from "class-variance-authority"
@@ -607,9 +608,7 @@ function SidebarMenuSkeleton({
showIcon?: boolean showIcon?: boolean
}) { }) {
// Random width between 50 to 90%. // Random width between 50 to 90%.
const width = React.useMemo(() => { const width = "70%";
return `${Math.floor(Math.random() * 40) + 50}%`
}, [])
return ( return (
<div <div
+1
View File
@@ -1,3 +1,4 @@
/* eslint-disable react-refresh/only-export-components */
import * as React from "react" import * as React from "react"
import * as TogglePrimitive from "@radix-ui/react-toggle" import * as TogglePrimitive from "@radix-ui/react-toggle"
import { cva, type VariantProps } from "class-variance-authority" import { cva, type VariantProps } from "class-variance-authority"
+130 -79
View File
@@ -15,9 +15,9 @@ import type {
Interconsulta, Interconsulta,
ATB, ATB,
Indicacion, Indicacion,
MovimientoIndicacion,
Vista, Vista,
Usuario, Usuario
RolUsuario
} from '@/types'; } from '@/types';
// Fallback UUID generator for non-secure contexts (HTTP without localhost) // Fallback UUID generator for non-secure contexts (HTTP without localhost)
@@ -47,7 +47,7 @@ interface HospitalState {
interconsultas: Interconsulta[]; interconsultas: Interconsulta[];
atb: ATB[]; atb: ATB[];
indicaciones: Indicacion[]; indicaciones: Indicacion[];
movimientosIndicaciones: any[]; movimientosIndicaciones: MovimientoIndicacion[];
vistaActual: Vista; vistaActual: Vista;
currentInternacionId?: string | null; currentInternacionId?: string | null;
usuarios: Usuario[]; usuarios: Usuario[];
@@ -108,7 +108,7 @@ export function useHospitalStore() {
}; };
setState(normalized); setState(normalized);
} }
} catch (err) { } catch {
// no remote state or unreachable — keep defaults // no remote state or unreachable — keep defaults
} finally { } finally {
if (mounted) setIsLoaded(true); if (mounted) setIsLoaded(true);
@@ -118,7 +118,7 @@ export function useHospitalStore() {
}, []); }, []);
// Helper to make API calls with error handling // Helper to make API calls with error handling
const apiCall = useCallback(async (method: string, endpoint: string, data?: any) => { const apiCall = useCallback(async (method: string, endpoint: string, data?: unknown) => {
try { try {
const res = await fetch(`${API_BASE}${endpoint}`, { const res = await fetch(`${API_BASE}${endpoint}`, {
method, method,
@@ -133,11 +133,6 @@ export function useHospitalStore() {
} }
}, []); }, []);
// Also save on specific events (optional)
const queueSave = (keys: string[]) => {
// Reserved for future implementation
};
// Utility functions // Utility functions
const getCamaGrupoId = useCallback((camaId: string): string | null => { const getCamaGrupoId = useCallback((camaId: string): string | null => {
const cama = state.camas.find(c => c.id === camaId); const cama = state.camas.find(c => c.id === camaId);
@@ -196,9 +191,13 @@ export function useHospitalStore() {
return (name || '').trim().toLowerCase().normalize('NFD').replace(/[\u0300-\u036f]/g, ''); return (name || '').trim().toLowerCase().normalize('NFD').replace(/[\u0300-\u036f]/g, '');
}; };
const userProfesionalName = getNombreProfesional(state.currentUser); const userProfesionalName = getNombreProfesional(state.currentUser);
const grupoId = getInternacionGrupoId(internacionId);
return state.currentUser?.rol === 'admin' || return state.currentUser?.rol === 'admin' ||
state.currentUser?.rol === 'medico' ||
canAccessGrupo(grupoId) ||
!internacion.medicoIngresante ||
(normalizeMedicoName(internacion.medicoIngresante) === normalizeMedicoName(userProfesionalName)); (normalizeMedicoName(internacion.medicoIngresante) === normalizeMedicoName(userProfesionalName));
}, [state.internaciones, state.currentUser]); }, [state.internaciones, state.currentUser, getInternacionGrupoId, canAccessGrupo]);
const canEditCama = useCallback((camaId: string): boolean => { const canEditCama = useCallback((camaId: string): boolean => {
const grupoId = getCamaGrupoId(camaId); const grupoId = getCamaGrupoId(camaId);
@@ -303,7 +302,7 @@ export function useHospitalStore() {
console.error('Error al actualizar cama:', err); console.error('Error al actualizar cama:', err);
throw err; throw err;
} }
}, [state, canAccessGrupo, apiCall]); }, [state, apiCall]);
const agregarCama = useCallback(async (cama: Omit<Cama, 'id'>) => { const agregarCama = useCallback(async (cama: Omit<Cama, 'id'>) => {
const nuevaCama: Cama = { const nuevaCama: Cama = {
@@ -348,15 +347,18 @@ const agregarCama = useCallback(async (cama: Omit<Cama, 'id'>) => {
id: generateUUID(), id: generateUUID(),
activa: true, activa: true,
}; };
try { try {
// API call inmediata para crear internación // API call inmediata para crear internación
await apiCall('POST', '/internaciones', nuevaInternacion); await apiCall('POST', '/internaciones', nuevaInternacion);
// Actualizar estado de la cama en la base de datos (solo estado) // Actualizar estado de la cama en la base de datos
if (internacion.camaId) {
await apiCall('PUT', `/camas/${internacion.camaId}`, { await apiCall('PUT', `/camas/${internacion.camaId}`, {
estado: 'Ocupada' estado: 'Ocupada',
pacienteId: internacion.pacienteId,
internacionId: nuevaInternacion.id
}); });
}
// Actualizar estado local después del éxito // Actualizar estado local después del éxito
setState(prev => ({ setState(prev => ({
@@ -364,7 +366,7 @@ const agregarCama = useCallback(async (cama: Omit<Cama, 'id'>) => {
internaciones: [...prev.internaciones, nuevaInternacion], internaciones: [...prev.internaciones, nuevaInternacion],
camas: prev.camas.map(c => camas: prev.camas.map(c =>
c.id === internacion.camaId c.id === internacion.camaId
? { ...c, estado: 'Ocupada' as const } ? { ...c, estado: 'Ocupada' as const, pacienteId: internacion.pacienteId, internacionId: nuevaInternacion.id }
: c : c
), ),
})); }));
@@ -374,7 +376,7 @@ const agregarCama = useCallback(async (cama: Omit<Cama, 'id'>) => {
console.error('Error al crear internación:', err); console.error('Error al crear internación:', err);
throw err; throw err;
} }
}, [apiCall]); }, [apiCall, checkGrupoPermission]);
const finalizarInternacion = useCallback(async (internacionId: string, datos: { const finalizarInternacion = useCallback(async (internacionId: string, datos: {
fechaEgreso: string; fechaEgreso: string;
@@ -401,7 +403,9 @@ const finalizarInternacion = useCallback(async (internacionId: string, datos: {
} else { } else {
// Marcar como disponible si es un área normal // Marcar como disponible si es un área normal
await apiCall('PUT', `/camas/${camaId}`, { await apiCall('PUT', `/camas/${camaId}`, {
estado: 'Disponible' estado: 'Disponible',
pacienteId: null,
internacionId: null
}); });
} }
} }
@@ -415,11 +419,11 @@ const finalizarInternacion = useCallback(async (internacionId: string, datos: {
? { ...i, ...datos, activa: false } ? { ...i, ...datos, activa: false }
: i : i
), ),
cams: esFueraDeGrupo camas: esFueraDeGrupo
? prev.camas.filter(c => c.id !== camaId) ? prev.camas.filter(c => c.id !== camaId)
: prev.camas.map(c => : prev.camas.map(c =>
c.id === camaId c.id === camaId
? { ...c, estado: 'Disponible' as const } ? { ...c, estado: 'Disponible' as const, pacienteId: undefined, internacionId: undefined }
: c : c
), ),
}; };
@@ -430,20 +434,70 @@ const finalizarInternacion = useCallback(async (internacionId: string, datos: {
} }
}, [state, apiCall]); }, [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;
try {
await apiCall('DELETE', `/internaciones/${internacionId}`);
if (camaId) {
if (esFueraDeGrupo) {
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: esFueraDeGrupo
? 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<Internacion>) => { const actualizarInternacion = useCallback(async (internacionId: string, datos: Partial<Internacion>) => {
const internacion = state.internaciones.find(i => i.id === internacionId); const internacion = state.internaciones.find(i => i.id === internacionId);
if (!internacion) return; if (!internacion) return;
// Check creator permission first // Check permissions
const normalizeMedicoName = (name?: string) => { if (!canEditIngreso(internacionId)) {
return (name || '').trim().toLowerCase().normalize('NFD').replace(/[\u0300-\u036f]/g, ''); toast.error(`No tiene permisos para modificar este ingreso.`);
};
const userProfesionalName = getNombreProfesional(state.currentUser);
const isCreator = state.currentUser?.rol === 'admin' ||
(normalizeMedicoName(internacion.medicoIngresante) === normalizeMedicoName(userProfesionalName));
if (!isCreator) {
toast.error(`Solo el usuario que creó esta internación (${internacion.medicoIngresante}) puede modificar el ingreso.`);
return; return;
} }
@@ -485,7 +539,7 @@ const actualizarInternacion = useCallback(async (internacionId: string, datos: P
? { ...i, ...datos } ? { ...i, ...datos }
: i : i
), ),
cams: prev.camas.map(c => { camas: prev.camas.map(c => {
if (c.id === newCamaId && newCamaId !== oldCamaId) { if (c.id === newCamaId && newCamaId !== oldCamaId) {
return { ...c, estado: 'Ocupada' as const }; return { ...c, estado: 'Ocupada' as const };
} }
@@ -501,7 +555,7 @@ const actualizarInternacion = useCallback(async (internacionId: string, datos: P
console.error('Error al actualizar internación:', err); console.error('Error al actualizar internación:', err);
throw err; throw err;
} }
}, [state, canAccessGrupo, checkGrupoPermission, apiCall]); }, [state, canChangeBed, canEditIngreso, checkGrupoPermission, apiCall]);
// Acciones de evoluciones // Acciones de evoluciones
const agregarEvolucion = useCallback(async (evolucion: Omit<Evolucion, 'id'>) => { const agregarEvolucion = useCallback(async (evolucion: Omit<Evolucion, 'id'>) => {
@@ -527,7 +581,7 @@ const actualizarInternacion = useCallback(async (internacionId: string, datos: P
console.error('Error al agregar evolución:', err); console.error('Error al agregar evolución:', err);
throw err; throw err;
} }
}, [state, canAccessGrupo, checkGrupoPermission, apiCall]); }, [state, checkGrupoPermission, apiCall]);
const eliminarEvolucion = useCallback(async (id: string) => { const eliminarEvolucion = useCallback(async (id: string) => {
const evolucion = state.evoluciones.find(e => e.id === id); const evolucion = state.evoluciones.find(e => e.id === id);
@@ -549,7 +603,7 @@ const actualizarInternacion = useCallback(async (internacionId: string, datos: P
console.error('Error al eliminar evolución:', err); console.error('Error al eliminar evolución:', err);
throw err; throw err;
} }
}, [state, canAccessGrupo, checkGrupoPermission, apiCall]); }, [state, checkGrupoPermission, apiCall]);
const actualizarEvolucion = useCallback(async (id: string, datos: Partial<Evolucion>) => { const actualizarEvolucion = useCallback(async (id: string, datos: Partial<Evolucion>) => {
const evolucion = state.evoluciones.find(e => e.id === id); const evolucion = state.evoluciones.find(e => e.id === id);
@@ -571,7 +625,7 @@ const actualizarInternacion = useCallback(async (internacionId: string, datos: P
console.error('Error al actualizar evolución:', err); console.error('Error al actualizar evolución:', err);
throw err; throw err;
} }
}, [state, canAccessGrupo, checkGrupoPermission, apiCall]); }, [state, checkGrupoPermission, apiCall]);
// Acciones de laboratorios // Acciones de laboratorios
const agregarLaboratorio = useCallback(async (laboratorio: Omit<Laboratorio, 'id'>) => { const agregarLaboratorio = useCallback(async (laboratorio: Omit<Laboratorio, 'id'>) => {
@@ -596,7 +650,7 @@ const actualizarInternacion = useCallback(async (internacionId: string, datos: P
console.error('Error al agregar laboratorio:', err); console.error('Error al agregar laboratorio:', err);
throw err; throw err;
} }
}, [state, canAccessGrupo, checkGrupoPermission, apiCall]); }, [state, checkGrupoPermission, apiCall]);
const eliminarLaboratorio = useCallback(async (id: string) => { const eliminarLaboratorio = useCallback(async (id: string) => {
const laboratorio = state.laboratorios.find(l => l.id === id); const laboratorio = state.laboratorios.find(l => l.id === id);
@@ -617,7 +671,7 @@ const actualizarInternacion = useCallback(async (internacionId: string, datos: P
console.error('Error al eliminar laboratorio:', err); console.error('Error al eliminar laboratorio:', err);
throw err; throw err;
} }
}, [state, canAccessGrupo, checkGrupoPermission, apiCall]); }, [state, checkGrupoPermission, apiCall]);
const actualizarLaboratorio = useCallback(async (id: string, datos: Partial<Laboratorio>) => { const actualizarLaboratorio = useCallback(async (id: string, datos: Partial<Laboratorio>) => {
const laboratorio = state.laboratorios.find(l => l.id === id); const laboratorio = state.laboratorios.find(l => l.id === id);
@@ -638,7 +692,7 @@ const actualizarInternacion = useCallback(async (internacionId: string, datos: P
console.error('Error al actualizar laboratorio:', err); console.error('Error al actualizar laboratorio:', err);
throw err; throw err;
} }
}, [state, canAccessGrupo, checkGrupoPermission, apiCall]); }, [state, checkGrupoPermission, apiCall]);
// Acciones de glucemias // Acciones de glucemias
const agregarGlucemia = useCallback(async (glucemia: Omit<Glucemia, 'id'>) => { const agregarGlucemia = useCallback(async (glucemia: Omit<Glucemia, 'id'>) => {
@@ -663,7 +717,7 @@ const actualizarInternacion = useCallback(async (internacionId: string, datos: P
console.error('Error al agregar glucemia:', err); console.error('Error al agregar glucemia:', err);
throw err; throw err;
} }
}, [state, canAccessGrupo, checkGrupoPermission, apiCall]); }, [state, checkGrupoPermission, apiCall]);
const eliminarGlucemia = useCallback(async (id: string) => { const eliminarGlucemia = useCallback(async (id: string) => {
const glucemia = state.glucemias.find(g => g.id === id); const glucemia = state.glucemias.find(g => g.id === id);
@@ -684,7 +738,7 @@ const actualizarInternacion = useCallback(async (internacionId: string, datos: P
console.error('Error al eliminar glucemia:', err); console.error('Error al eliminar glucemia:', err);
throw err; throw err;
} }
}, [state, canAccessGrupo, checkGrupoPermission, apiCall]); }, [state, checkGrupoPermission, apiCall]);
const actualizarGlucemia = useCallback(async (id: string, datos: Partial<Glucemia>) => { const actualizarGlucemia = useCallback(async (id: string, datos: Partial<Glucemia>) => {
const glucemia = state.glucemias.find(g => g.id === id); const glucemia = state.glucemias.find(g => g.id === id);
@@ -705,7 +759,7 @@ const actualizarInternacion = useCallback(async (internacionId: string, datos: P
console.error('Error al actualizar glucemia:', err); console.error('Error al actualizar glucemia:', err);
throw err; throw err;
} }
}, [state, canAccessGrupo, checkGrupoPermission, apiCall]); }, [state, checkGrupoPermission, apiCall]);
// Acciones de ácido-base // Acciones de ácido-base
const agregarAcidoBase = useCallback(async (acidoBase: Omit<AcidoBase, 'id'>) => { const agregarAcidoBase = useCallback(async (acidoBase: Omit<AcidoBase, 'id'>) => {
@@ -730,7 +784,7 @@ const actualizarInternacion = useCallback(async (internacionId: string, datos: P
console.error('Error al agregar ácido-base:', err); console.error('Error al agregar ácido-base:', err);
throw err; throw err;
} }
}, [state, canAccessGrupo, checkGrupoPermission, apiCall]); }, [state, checkGrupoPermission, apiCall]);
const actualizarAcidoBase = useCallback(async (id: string, datos: Partial<AcidoBase>) => { const actualizarAcidoBase = useCallback(async (id: string, datos: Partial<AcidoBase>) => {
const acidoBase = state.acidosBase.find(a => a.id === id); const acidoBase = state.acidosBase.find(a => a.id === id);
@@ -751,7 +805,7 @@ const actualizarInternacion = useCallback(async (internacionId: string, datos: P
console.error('Error al actualizar ácido-base:', err); console.error('Error al actualizar ácido-base:', err);
throw err; throw err;
} }
}, [state, canAccessGrupo, checkGrupoPermission, apiCall]); }, [state, checkGrupoPermission, apiCall]);
const eliminarAcidoBase = useCallback(async (id: string) => { const eliminarAcidoBase = useCallback(async (id: string) => {
const acidoBase = state.acidosBase.find(a => a.id === id); const acidoBase = state.acidosBase.find(a => a.id === id);
@@ -772,7 +826,7 @@ const actualizarInternacion = useCallback(async (internacionId: string, datos: P
console.error('Error al eliminar ácido-base:', err); console.error('Error al eliminar ácido-base:', err);
throw err; throw err;
} }
}, [state, canAccessGrupo, checkGrupoPermission, apiCall]); }, [state, checkGrupoPermission, apiCall]);
// Acciones de cultivos // Acciones de cultivos
const agregarCultivo = useCallback(async (cultivo: Omit<Cultivo, 'id'>) => { const agregarCultivo = useCallback(async (cultivo: Omit<Cultivo, 'id'>) => {
@@ -797,7 +851,7 @@ const actualizarInternacion = useCallback(async (internacionId: string, datos: P
console.error('Error al agregar cultivo:', err); console.error('Error al agregar cultivo:', err);
throw err; throw err;
} }
}, [state, canAccessGrupo, checkGrupoPermission, apiCall]); }, [state, checkGrupoPermission, apiCall]);
const actualizarCultivo = useCallback(async (id: string, datos: Partial<Cultivo>) => { const actualizarCultivo = useCallback(async (id: string, datos: Partial<Cultivo>) => {
const cultivo = state.cultivos.find(c => c.id === id); const cultivo = state.cultivos.find(c => c.id === id);
@@ -818,7 +872,7 @@ const actualizarInternacion = useCallback(async (internacionId: string, datos: P
console.error('Error al actualizar cultivo:', err); console.error('Error al actualizar cultivo:', err);
throw err; throw err;
} }
}, [state, canAccessGrupo, checkGrupoPermission, apiCall]); }, [state, checkGrupoPermission, apiCall]);
const eliminarCultivo = useCallback(async (id: string) => { const eliminarCultivo = useCallback(async (id: string) => {
const cultivo = state.cultivos.find(c => c.id === id); const cultivo = state.cultivos.find(c => c.id === id);
@@ -839,7 +893,7 @@ const actualizarInternacion = useCallback(async (internacionId: string, datos: P
console.error('Error al eliminar cultivo:', err); console.error('Error al eliminar cultivo:', err);
throw err; throw err;
} }
}, [state, canAccessGrupo, checkGrupoPermission, apiCall]); }, [state, checkGrupoPermission, apiCall]);
// Acciones de estudios complementarios // Acciones de estudios complementarios
const agregarEstudioComplementario = useCallback(async (estudio: Omit<EstudioComplementario, 'id'>) => { const agregarEstudioComplementario = useCallback(async (estudio: Omit<EstudioComplementario, 'id'>) => {
@@ -864,7 +918,7 @@ const actualizarInternacion = useCallback(async (internacionId: string, datos: P
console.error('Error al agregar estudio:', err); console.error('Error al agregar estudio:', err);
throw err; throw err;
} }
}, [state, canAccessGrupo, checkGrupoPermission, apiCall]); }, [state, checkGrupoPermission, apiCall]);
const actualizarEstudioComplementario = useCallback(async (id: string, datos: Partial<EstudioComplementario>) => { const actualizarEstudioComplementario = useCallback(async (id: string, datos: Partial<EstudioComplementario>) => {
const estudio = state.estudiosComplementarios.find(e => e.id === id); const estudio = state.estudiosComplementarios.find(e => e.id === id);
@@ -885,7 +939,7 @@ const actualizarInternacion = useCallback(async (internacionId: string, datos: P
console.error('Error al actualizar estudio:', err); console.error('Error al actualizar estudio:', err);
throw err; throw err;
} }
}, [state, canAccessGrupo, checkGrupoPermission, apiCall]); }, [state, checkGrupoPermission, apiCall]);
const eliminarEstudioComplementario = useCallback(async (id: string) => { const eliminarEstudioComplementario = useCallback(async (id: string) => {
const estudio = state.estudiosComplementarios.find(e => e.id === id); const estudio = state.estudiosComplementarios.find(e => e.id === id);
@@ -906,7 +960,7 @@ const actualizarInternacion = useCallback(async (internacionId: string, datos: P
console.error('Error al eliminar estudio:', err); console.error('Error al eliminar estudio:', err);
throw err; throw err;
} }
}, [state, canAccessGrupo, checkGrupoPermission, apiCall]); }, [state, checkGrupoPermission, apiCall]);
// Acciones de interconsultas // Acciones de interconsultas
const agregarInterconsulta = useCallback(async (interconsulta: Omit<Interconsulta, 'id'>) => { const agregarInterconsulta = useCallback(async (interconsulta: Omit<Interconsulta, 'id'>) => {
@@ -928,7 +982,7 @@ const actualizarInternacion = useCallback(async (internacionId: string, datos: P
console.error('Error al agregar interconsulta:', err); console.error('Error al agregar interconsulta:', err);
throw err; throw err;
} }
}, [state, canAccessGrupo, checkGrupoPermission, apiCall]); }, [state, checkGrupoPermission, apiCall]);
const actualizarInterconsulta = useCallback(async (id: string, datos: Partial<Interconsulta>) => { const actualizarInterconsulta = useCallback(async (id: string, datos: Partial<Interconsulta>) => {
const interconsulta = (state.interconsultas || []).find(ic => ic.id === id); const interconsulta = (state.interconsultas || []).find(ic => ic.id === id);
@@ -949,7 +1003,7 @@ const actualizarInternacion = useCallback(async (internacionId: string, datos: P
console.error('Error al actualizar interconsulta:', err); console.error('Error al actualizar interconsulta:', err);
throw err; throw err;
} }
}, [state, canAccessGrupo, checkGrupoPermission, apiCall]); }, [state, checkGrupoPermission, apiCall]);
const eliminarInterconsulta = useCallback(async (id: string) => { const eliminarInterconsulta = useCallback(async (id: string) => {
const interconsulta = (state.interconsultas || []).find(ic => ic.id === id); const interconsulta = (state.interconsultas || []).find(ic => ic.id === id);
@@ -970,7 +1024,7 @@ const actualizarInternacion = useCallback(async (internacionId: string, datos: P
console.error('Error al eliminar interconsulta:', err); console.error('Error al eliminar interconsulta:', err);
throw err; throw err;
} }
}, [state, canAccessGrupo, checkGrupoPermission, apiCall]); }, [state, checkGrupoPermission, apiCall]);
const agregarATB = useCallback(async (atb: Omit<ATB, 'id'>) => { const agregarATB = useCallback(async (atb: Omit<ATB, 'id'>) => {
const internacion = state.internaciones.find(i => i.id === atb.internacionId); const internacion = state.internaciones.find(i => i.id === atb.internacionId);
@@ -991,7 +1045,7 @@ const actualizarInternacion = useCallback(async (internacionId: string, datos: P
console.error('Error al agregar ATB:', err); console.error('Error al agregar ATB:', err);
throw err; throw err;
} }
}, [state, canAccessGrupo, checkGrupoPermission, apiCall]); }, [state, checkGrupoPermission, apiCall]);
const actualizarATB = useCallback(async (id: string, datos: Partial<ATB>) => { const actualizarATB = useCallback(async (id: string, datos: Partial<ATB>) => {
const atb = state.atb.find(a => a.id === id); const atb = state.atb.find(a => a.id === id);
@@ -1012,7 +1066,7 @@ const actualizarInternacion = useCallback(async (internacionId: string, datos: P
console.error('Error al actualizar ATB:', err); console.error('Error al actualizar ATB:', err);
throw err; throw err;
} }
}, [state, canAccessGrupo, checkGrupoPermission, apiCall]); }, [state, checkGrupoPermission, apiCall]);
const eliminarATB = useCallback(async (id: string) => { const eliminarATB = useCallback(async (id: string) => {
const atb = state.atb.find(a => a.id === id); const atb = state.atb.find(a => a.id === id);
@@ -1033,7 +1087,7 @@ const actualizarInternacion = useCallback(async (internacionId: string, datos: P
console.error('Error al eliminar ATB:', err); console.error('Error al eliminar ATB:', err);
throw err; throw err;
} }
}, [state, canAccessGrupo, checkGrupoPermission, apiCall]); }, [state, checkGrupoPermission, apiCall]);
const agregarIndicacion = useCallback(async (indicacion: Omit<Indicacion, 'id'>) => { const agregarIndicacion = useCallback(async (indicacion: Omit<Indicacion, 'id'>) => {
const internacion = state.internaciones.find(i => i.id === indicacion.internacionId); const internacion = state.internaciones.find(i => i.id === indicacion.internacionId);
@@ -1054,7 +1108,7 @@ const actualizarInternacion = useCallback(async (internacionId: string, datos: P
console.error('Error al agregar indicación:', err); console.error('Error al agregar indicación:', err);
throw err; throw err;
} }
}, [state.internaciones, canAccessGrupo, checkGrupoPermission, apiCall]); }, [state.internaciones, checkGrupoPermission, apiCall]);
const actualizarIndicacion = useCallback(async (id: string, datos: Partial<Indicacion>) => { const actualizarIndicacion = useCallback(async (id: string, datos: Partial<Indicacion>) => {
const indicacion = state.indicaciones.find(i => i.id === id); const indicacion = state.indicaciones.find(i => i.id === id);
@@ -1075,7 +1129,7 @@ const actualizarInternacion = useCallback(async (internacionId: string, datos: P
console.error('Error al actualizar indicación:', err); console.error('Error al actualizar indicación:', err);
throw err; throw err;
} }
}, [state.indicaciones, state.internaciones, canAccessGrupo, checkGrupoPermission, apiCall]); }, [state.indicaciones, state.internaciones, checkGrupoPermission, apiCall]);
const eliminarIndicacion = useCallback(async (id: string) => { const eliminarIndicacion = useCallback(async (id: string) => {
const indicacion = state.indicaciones.find(i => i.id === id); const indicacion = state.indicaciones.find(i => i.id === id);
@@ -1096,9 +1150,9 @@ const actualizarInternacion = useCallback(async (internacionId: string, datos: P
console.error('Error al eliminar indicación:', err); console.error('Error al eliminar indicación:', err);
throw err; throw err;
} }
}, [state.indicaciones, state.internaciones, canAccessGrupo, checkGrupoPermission, apiCall]); }, [state.indicaciones, state.internaciones, checkGrupoPermission, apiCall]);
const agregarMovimientoIndicacion = useCallback(async (movimiento: any) => { const agregarMovimientoIndicacion = useCallback(async (movimiento: MovimientoIndicacion) => {
const internacion = state.internaciones.find(i => i.id === movimiento.internacionId); const internacion = state.internaciones.find(i => i.id === movimiento.internacionId);
const grupoId = internacion?.grupoId || null; const grupoId = internacion?.grupoId || null;
if (!checkGrupoPermission(grupoId)) { if (!checkGrupoPermission(grupoId)) {
@@ -1116,12 +1170,12 @@ const actualizarInternacion = useCallback(async (internacionId: string, datos: P
console.error('Error al agregar movimiento:', err); console.error('Error al agregar movimiento:', err);
throw err; throw err;
} }
}, [state, canAccessGrupo, checkGrupoPermission, apiCall]); }, [state, checkGrupoPermission, apiCall]);
const getMovimientosByInternacion = useCallback((internacionId: string) => { const getMovimientosByInternacion = useCallback((internacionId: string) => {
return (state.movimientosIndicaciones || []) return (state.movimientosIndicaciones || [])
.filter((m: any) => m.internacionId === internacionId) .filter((m: MovimientoIndicacion) => m.internacionId === internacionId)
.sort((a: any, b: any) => new Date(b.fecha).getTime() - new Date(a.fecha).getTime()); .sort((a: MovimientoIndicacion, b: MovimientoIndicacion) => new Date(b.fecha).getTime() - new Date(a.fecha).getTime());
}, [state.movimientosIndicaciones]); }, [state.movimientosIndicaciones]);
// Acciones de grupos // Acciones de grupos
@@ -1190,27 +1244,27 @@ const actualizarInternacion = useCallback(async (internacionId: string, datos: P
.sort((a, b) => new Date(b.fecha + 'T' + b.hora).getTime() - new Date(a.fecha + 'T' + a.hora).getTime()); .sort((a, b) => new Date(b.fecha + 'T' + b.hora).getTime() - new Date(a.fecha + 'T' + a.hora).getTime());
}, [state.evoluciones]); }, [state.evoluciones]);
const getLaboratoriosByPaciente = useCallback((pacienteId: string) => { const getLaboratoriosByInternacion = useCallback((internacionId: string) => {
return state.laboratorios return state.laboratorios
.filter(l => l.pacienteId === pacienteId) .filter(l => l.internacionId === internacionId)
.sort((a, b) => new Date(b.fecha).getTime() - new Date(a.fecha).getTime()); .sort((a, b) => new Date(b.fecha).getTime() - new Date(a.fecha).getTime());
}, [state.laboratorios]); }, [state.laboratorios]);
const getGlucemiasByPaciente = useCallback((pacienteId: string) => { const getGlucemiasByInternacion = useCallback((internacionId: string) => {
return state.glucemias return state.glucemias
.filter(g => g.pacienteId === pacienteId) .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()); .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]); }, [state.glucemias]);
const getAcidosBaseByPaciente = useCallback((pacienteId: string) => { const getAcidosBaseByInternacion = useCallback((internacionId: string) => {
return state.acidosBase return state.acidosBase
.filter(a => a.pacienteId === pacienteId) .filter(a => a.internacionId === internacionId)
.sort((a, b) => new Date(b.fecha + 'T' + b.hora).getTime() - new Date(a.fecha + 'T' + a.hora).getTime()); .sort((a, b) => new Date(b.fecha + 'T' + b.hora).getTime() - new Date(a.fecha + 'T' + a.hora).getTime());
}, [state.acidosBase]); }, [state.acidosBase]);
const getCultivosByPaciente = useCallback((pacienteId: string) => { const getCultivosByInternacion = useCallback((internacionId: string) => {
return state.cultivos return state.cultivos
.filter(c => c.pacienteId === pacienteId) .filter(c => c.internacionId === internacionId)
.sort((a, b) => new Date(b.fechaToma).getTime() - new Date(a.fechaToma).getTime()); .sort((a, b) => new Date(b.fechaToma).getTime() - new Date(a.fechaToma).getTime());
}, [state.cultivos]); }, [state.cultivos]);
@@ -1247,7 +1301,6 @@ const actualizarInternacion = useCallback(async (internacionId: string, datos: P
// Auth functions // Auth functions
const login = useCallback(async (dni: string, password: string) => { const login = useCallback(async (dni: string, password: string) => {
try {
const res = await fetch(`${API_BASE}/auth/login`, { const res = await fetch(`${API_BASE}/auth/login`, {
method: 'POST', method: 'POST',
headers: { 'Content-Type': 'application/json' }, headers: { 'Content-Type': 'application/json' },
@@ -1261,9 +1314,6 @@ const actualizarInternacion = useCallback(async (internacionId: string, datos: P
sessionStorage.setItem('hospital_user', JSON.stringify(user)); sessionStorage.setItem('hospital_user', JSON.stringify(user));
setState(prev => ({ ...prev, currentUser: user, isAuthenticated: true })); setState(prev => ({ ...prev, currentUser: user, isAuthenticated: true }));
return user; return user;
} catch (err) {
throw err;
}
}, []); }, []);
const logout = useCallback(() => { const logout = useCallback(() => {
@@ -1311,6 +1361,7 @@ const actualizarInternacion = useCallback(async (internacionId: string, datos: P
iniciarInternacion, iniciarInternacion,
finalizarInternacion, finalizarInternacion,
actualizarInternacion, actualizarInternacion,
eliminarInternacion,
agregarCama, agregarCama,
eliminarCama, eliminarCama,
agregarEvolucion, agregarEvolucion,
@@ -1350,10 +1401,10 @@ const actualizarInternacion = useCallback(async (internacionId: string, datos: P
getInternacionById, getInternacionById,
getInternacionActivaByPaciente, getInternacionActivaByPaciente,
getEvolucionesByInternacion, getEvolucionesByInternacion,
getLaboratoriosByPaciente, getLaboratoriosByInternacion,
getGlucemiasByPaciente, getGlucemiasByInternacion,
getAcidosBaseByPaciente, getAcidosBaseByInternacion,
getCultivosByPaciente, getCultivosByInternacion,
setCurrentInternacion, setCurrentInternacion,
getEstadisticas, getEstadisticas,
login, login,
+1 -2
View File
@@ -40,8 +40,7 @@ export function calcularEdad(fechaNacimiento?: string): string | number {
export function isCamaFueraDeGrupo( export function isCamaFueraDeGrupo(
cama: { numero: string; grupoId?: string; areaId?: string; sector?: string }, cama: { numero: string; grupoId?: string; areaId?: string; sector?: string }
_gruposOrAreas?: { id: string; nombre: string }[]
): boolean { ): boolean {
if (cama.sector === 'Fuera de Área') return true; if (cama.sector === 'Fuera de Área') return true;
if (cama.sector === 'En Área') return false; if (cama.sector === 'En Área') return false;
+3 -7
View File
@@ -181,10 +181,6 @@ export function Cultivos({
setDialogoDefinitivoAbierto(false); setDialogoDefinitivoAbierto(false);
}; };
const getNumeroCama = (pacienteId: string) => {
return '';
};
return ( return (
<div className="space-y-4"> <div className="space-y-4">
<div className="flex flex-col sm:flex-row gap-4 items-start sm:items-center justify-between"> <div className="flex flex-col sm:flex-row gap-4 items-start sm:items-center justify-between">
@@ -198,7 +194,7 @@ export function Cultivos({
className="pl-8" className="pl-8"
/> />
</div> </div>
<Select value={filtroEstado} onValueChange={(v) => setFiltroEstado(v as any)}> <Select value={filtroEstado} onValueChange={(v) => setFiltroEstado(v as Cultivo['estado'] | 'todos')}>
<SelectTrigger className="w-[180px]"> <SelectTrigger className="w-[180px]">
<SelectValue /> <SelectValue />
</SelectTrigger> </SelectTrigger>
@@ -410,7 +406,7 @@ export function Cultivos({
</div> </div>
<div className="space-y-2"> <div className="space-y-2">
<Label>Tipo de Muestra</Label> <Label>Tipo de Muestra</Label>
<Select value={tipoMuestra} onValueChange={(v) => setTipoMuestra(v as any)}> <Select value={tipoMuestra} onValueChange={(v) => setTipoMuestra(v as Cultivo['tipoMuestra'])}>
<SelectTrigger> <SelectTrigger>
<SelectValue /> <SelectValue />
</SelectTrigger> </SelectTrigger>
@@ -485,7 +481,7 @@ export function Cultivos({
</div> </div>
<div className="space-y-2"> <div className="space-y-2">
<Label>Estado</Label> <Label>Estado</Label>
<Select value={estadoResultado} onValueChange={(v) => setEstadoResultado(v as any)}> <Select value={estadoResultado} onValueChange={(v) => setEstadoResultado(v as Cultivo['estado'])}>
<SelectTrigger> <SelectTrigger>
<SelectValue /> <SelectValue />
</SelectTrigger> </SelectTrigger>
+4 -2
View File
@@ -1,5 +1,5 @@
import { useState } from 'react'; import { useState } from 'react';
import { Save, X, Bed, Stethoscope, User, Loader2 } from 'lucide-react'; import { Save, X, Bed, Stethoscope, User, Loader2, ClipboardList } from 'lucide-react';
import { Card, CardContent } from '@/components/ui/card'; import { Card, CardContent } from '@/components/ui/card';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input'; import { Input } from '@/components/ui/input';
@@ -32,12 +32,14 @@ export function EditIngreso({
onActualizarInternacion, onActualizarInternacion,
onAgregarCama, onAgregarCama,
onVolver, onVolver,
getGrupoName,
}: EditIngresoProps) { }: EditIngresoProps) {
const { currentUser } = useHospitalStore(); const { currentUser } = useHospitalStore();
const [pacienteSeleccionado] = useState(internacion.pacienteId); const [pacienteSeleccionado] = useState(internacion.pacienteId);
const [camaSeleccionada] = useState(internacion.camaId); const [camaSeleccionada, setCamaSeleccionada] = useState(internacion.camaId);
const [grupoSeleccionada] = useState(internacion.grupoId); const [grupoSeleccionada] = useState(internacion.grupoId);
const [camaInput, setCamaInput] = useState(''); const [camaInput, setCamaInput] = useState('');
const [busqueda, setBusqueda] = useState('');
const [modoCama, setModoCama] = useState<'seleccionar' | 'escribir'>('seleccionar'); const [modoCama, setModoCama] = useState<'seleccionar' | 'escribir'>('seleccionar');
const effectiveMedico = getNombreProfesional(currentUser); const effectiveMedico = getNombreProfesional(currentUser);
const [fechaIngresoHospital, setFechaIngresoHospital] = useState(internacion.fechaIngresoHospital || ''); const [fechaIngresoHospital, setFechaIngresoHospital] = useState(internacion.fechaIngresoHospital || '');
-11
View File
@@ -203,17 +203,6 @@ export function Evoluciones({
}) })
.sort((a, b) => new Date(b.fecha + 'T' + b.hora).getTime() - new Date(a.fecha + 'T' + a.hora).getTime()); .sort((a, b) => new Date(b.fecha + 'T' + b.hora).getTime() - new Date(a.fecha + 'T' + a.hora).getTime());
const getSignosVitalesTexto = (sv: SignosVitales | undefined) => {
if (!sv) return null;
const partes: string[] = [];
if (sv.presionSistolica && sv.presionDiastolica) partes.push(`PA: ${sv.presionSistolica}/${sv.presionDiastolica}`);
if (sv.frecuenciaCardiaca) partes.push(`FC: ${sv.frecuenciaCardiaca}`);
if (sv.frecuenciaRespiratoria) partes.push(`FR: ${sv.frecuenciaRespiratoria}`);
if (sv.temperatura) partes.push(`T: ${sv.temperatura}°C`);
if (sv.saturacionO2) partes.push(`SatO2: ${sv.saturacionO2}%`);
return partes.join(' | ');
};
return ( return (
<div className="space-y-6 dark:text-white overflow-x-hidden max-w-screen"> <div className="space-y-6 dark:text-white overflow-x-hidden max-w-screen">
<div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4"> <div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4">
+23 -97
View File
@@ -1,6 +1,5 @@
import { useState } from 'react'; import { useState } from 'react';
import { toast } from 'sonner'; import { toast } from 'sonner';
import { PDFDocument } from 'pdf-lib';
import { User } from 'lucide-react'; import { User } from 'lucide-react';
import { useHospitalStore } from '@/hooks/useHospitalStore'; import { useHospitalStore } from '@/hooks/useHospitalStore';
import { getNombreProfesional } from '@/lib/utils'; import { getNombreProfesional } from '@/lib/utils';
@@ -17,15 +16,12 @@ import {
Plus, Plus,
Calendar, Calendar,
Droplet, Droplet,
Clock,
AlertCircle, AlertCircle,
CheckCircle2, CheckCircle2,
ArrowLeft, ArrowLeft,
Pencil, Pencil,
Trash2, Trash2,
FileText, FileText,
ChevronDown,
ChevronUp,
List, List,
FileDown, FileDown,
Thermometer, Thermometer,
@@ -39,7 +35,6 @@ import {
Edit, Edit,
ClipboardList, ClipboardList,
TrendingUp, TrendingUp,
TypeOutline,
Activity, Activity,
Pill, Pill,
Users Users
@@ -49,14 +44,14 @@ import { Badge } from '@/components/ui/badge';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input'; import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label'; import { Label } from '@/components/ui/label';
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from '@/components/ui/dialog'; import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table'; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table';
import { ScrollArea, ScrollBar } from "@/components/ui/scroll-area" import { ScrollArea, ScrollBar } from "@/components/ui/scroll-area"
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from '@/components/ui/dropdown-menu'; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from '@/components/ui/dropdown-menu';
import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer } from 'recharts'; import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer } from 'recharts';
import type { Laboratorio, Glucemia, AcidoBase, Cultivo, Paciente, ResultadoLaboratorio, Internacion, Evolucion, Cama, EstudioComplementario, Interconsulta, ATB, Indicacion } from '@/types'; import type { Laboratorio, Glucemia, AcidoBase, Cultivo, Paciente, ResultadoLaboratorio, Internacion, Evolucion, Cama, EstudioComplementario, Interconsulta, ATB, Indicacion, MovimientoIndicacion } from '@/types';
import { formatDateDDMMYYYY } from '@/lib/utils'; import { formatDateDDMMYYYY } from '@/lib/utils';
import { SeccionIndicaciones } from './SeccionIndicaciones'; import { SeccionIndicaciones } from './SeccionIndicaciones';
@@ -64,7 +59,6 @@ interface HistoriaClinicaProps {
internacion: Internacion; internacion: Internacion;
paciente: Paciente; paciente: Paciente;
cama?: Cama; cama?: Cama;
allCamas?: Cama[];
evoluciones: Evolucion[]; evoluciones: Evolucion[];
laboratorios: Laboratorio[]; laboratorios: Laboratorio[];
glucemias: Glucemia[]; glucemias: Glucemia[];
@@ -74,7 +68,7 @@ interface HistoriaClinicaProps {
interconsultas: Interconsulta[]; interconsultas: Interconsulta[];
atb: ATB[]; atb: ATB[];
indicadores: Indicacion[]; indicadores: Indicacion[];
movimientos?: any[]; movimientos?: MovimientoIndicacion[];
canEdit: boolean; canEdit: boolean;
onAgregarEvolucion: (evolucion: Omit<Evolucion, 'id'>) => void; onAgregarEvolucion: (evolucion: Omit<Evolucion, 'id'>) => void;
onActualizarEvolucion: (id: string, datos: Partial<Evolucion>) => void; onActualizarEvolucion: (id: string, datos: Partial<Evolucion>) => void;
@@ -104,33 +98,11 @@ interface HistoriaClinicaProps {
onAgregarIndicacion: (indicacion: Omit<Indicacion, 'id'>) => void; onAgregarIndicacion: (indicacion: Omit<Indicacion, 'id'>) => void;
onActualizarIndicacion: (id: string, datos: Partial<Indicacion>) => void; onActualizarIndicacion: (id: string, datos: Partial<Indicacion>) => void;
onEliminarIndicacion: (id: string) => void; onEliminarIndicacion: (id: string) => void;
onAgregarMovimiento?: (movimiento: any) => void; onAgregarMovimiento?: (movimiento: MovimientoIndicacion) => void;
onActualizarInternacion: (id: string, datos: Partial<Internacion>) => void;
onActualizarCama: (id: string, datos: Partial<Cama>) => void;
onVolver: () => void; onVolver: () => void;
onEditarIngreso?: () => void; onEditarIngreso?: () => void;
} }
const PARAMETROS_COMUNES: Record<string, { unidad: string; referencia: string }> = {
'Hemoglobina': { unidad: 'g/dL', referencia: '12-14' },
'Hematocrito': { unidad: '%', referencia: '36-42' },
'Glóbulos Blancos': { unidad: 'cel/μL', referencia: '4000-11000' },
'Plaquetas': { unidad: 'unidades/μL', referencia: '150000-400000' },
'Glucosa': { unidad: 'mg/dL', referencia: '70-100' },
'Urea': { unidad: 'mg/dL', referencia: '17-48.5' },
'Creatinina': { unidad: 'mg/dL', referencia: '0.5-1' },
'Sodio': { unidad: 'mEq/L', referencia: '135-145' },
'Potasio': { unidad: 'mEq/L', referencia: '3.5-5.1' },
'Cloro': { unidad: 'mEq/L', referencia: '101-109' },
'Bilirrubina Total': { unidad: 'mg/dL', referencia: '0-1.2' },
'Bilirrubina Directa': { unidad: 'mg/dL', referencia: '0-0.3' },
'TGO/AST': { unidad: 'U/L', referencia: '0-35' },
'TGP/ALT': { unidad: 'U/L', referencia: '0-35' },
'TP': { unidad: '%', referencia: '70-100' },
'KPTT': { unidad: 'seg', referencia: '30-45' },
'RIN': { unidad: '', referencia: '0.8-1.5' },
};
const RANGOS_LABORATORIO: Record<string, { min: number; max: number }> = { const RANGOS_LABORATORIO: Record<string, { min: number; max: number }> = {
'Hematocrito': { min: 36, max: 42 }, 'Hematocrito': { min: 36, max: 42 },
'Hemoglobina': { min: 12, max: 14 }, 'Hemoglobina': { min: 12, max: 14 },
@@ -201,7 +173,7 @@ function calcularEstadoLaboratorio(parametro: string, valor: string): 'Normal' |
return 'Normal'; return 'Normal';
} }
function wrapText(text: string, font: any, fontSize: number, maxWidth: number): string[] { function wrapText(text: string, font: { widthOfTextAtSize: (text: string, size: number) => number }, fontSize: number, maxWidth: number): string[] {
const words = text.split(' '); const words = text.split(' ');
const lines: string[] = []; const lines: string[] = [];
let currentLine = ''; let currentLine = '';
@@ -226,7 +198,6 @@ export function HistoriaClinica({
internacion, internacion,
paciente, paciente,
cama, cama,
allCamas,
evoluciones, evoluciones,
laboratorios, laboratorios,
glucemias, glucemias,
@@ -265,8 +236,6 @@ export function HistoriaClinica({
onEliminarIndicacion, onEliminarIndicacion,
movimientos, movimientos,
onAgregarMovimiento, onAgregarMovimiento,
onActualizarInternacion,
onActualizarCama,
onVolver, onVolver,
onEditarIngreso, onEditarIngreso,
canEdit, canEdit,
@@ -590,11 +559,11 @@ export function HistoriaClinica({
</ScrollArea> </ScrollArea>
<TabsContent value="glucemias" className="mt-4 min-w-0 w-full overflow-hidden"> <TabsContent value="glucemias" className="mt-4 min-w-0 w-full overflow-hidden">
<SeccionGlucemias glucemias={glucemias} patientId={paciente.id} add={onAgregarGlucemia} update={onActualizarGlucemia} del={onEliminarGlucemia} canEdit={canEdit} /> <SeccionGlucemias glucemias={glucemias} patientId={paciente.id} internacionId={internacion.id} add={onAgregarGlucemia} update={onActualizarGlucemia} del={onEliminarGlucemia} canEdit={canEdit} />
</TabsContent> </TabsContent>
<TabsContent value="laboratorios" className="mt-4 min-w-0 w-full overflow-hidden"> <TabsContent value="laboratorios" className="mt-4 min-w-0 w-full overflow-hidden">
<SeccionLaboratorios lab={laboratorios} patientId={paciente.id} add={onAgregarLaboratorio} update={onActualizarLaboratorio} del={onEliminarLaboratorio} addAcidoBase={onAgregarAcidoBase} canEdit={canEdit} /> <SeccionLaboratorios lab={laboratorios} patientId={paciente.id} internacionId={internacion.id} add={onAgregarLaboratorio} update={onActualizarLaboratorio} del={onEliminarLaboratorio} addAcidoBase={onAgregarAcidoBase} canEdit={canEdit} />
</TabsContent> </TabsContent>
<TabsContent value="evoluciones" className="mt-4"> <TabsContent value="evoluciones" className="mt-4">
@@ -602,11 +571,11 @@ export function HistoriaClinica({
</TabsContent> </TabsContent>
<TabsContent value="acidobase" className="mt-4"> <TabsContent value="acidobase" className="mt-4">
<SeccionAcidosBase ab={acidosBase} patientId={paciente.id} add={onAgregarAcidoBase} update={onActualizarAcidoBase} del={onEliminarAcidoBase} canEdit={canEdit} /> <SeccionAcidosBase ab={acidosBase} patientId={paciente.id} internacionId={internacion.id} add={onAgregarAcidoBase} update={onActualizarAcidoBase} del={onEliminarAcidoBase} canEdit={canEdit} />
</TabsContent> </TabsContent>
<TabsContent value="cultivos" className="mt-4"> <TabsContent value="cultivos" className="mt-4">
<SeccionCultivos cults={cultivos} patient={paciente} add={onAgregarCultivo} update={onActualizarCultivo} del={onEliminarCultivo} canEdit={canEdit} /> <SeccionCultivos cults={cultivos} patient={paciente} internacionId={internacion.id} add={onAgregarCultivo} update={onActualizarCultivo} del={onEliminarCultivo} canEdit={canEdit} />
</TabsContent> </TabsContent>
<TabsContent value="atb" className="mt-4"> <TabsContent value="atb" className="mt-4">
@@ -668,9 +637,10 @@ export function HistoriaClinica({
); );
} }
function SeccionGlucemias({ glucemias, patientId, add, update, del, canEdit }: { function SeccionGlucemias({ glucemias, patientId, internacionId, add, update, del, canEdit }: {
glucemias: Glucemia[]; glucemias: Glucemia[];
patientId: string; patientId: string;
internacionId: string;
add: (g: Omit<Glucemia, 'id'>) => void; add: (g: Omit<Glucemia, 'id'>) => void;
update: (id: string, data: Partial<Glucemia>) => void; update: (id: string, data: Partial<Glucemia>) => void;
del: (id: string) => void; del: (id: string) => void;
@@ -725,6 +695,7 @@ function SeccionGlucemias({ glucemias, patientId, add, update, del, canEdit }: {
} else { } else {
add({ add({
pacienteId: patientId, pacienteId: patientId,
internacionId,
fecha, fecha,
hora, hora,
valor: valNum, valor: valNum,
@@ -926,9 +897,10 @@ function SeccionGlucemias({ glucemias, patientId, add, update, del, canEdit }: {
); );
} }
function SeccionLaboratorios({ lab, patientId, add, update, del, addAcidoBase, canEdit }: { function SeccionLaboratorios({ lab, patientId, internacionId, add, update, del, addAcidoBase, canEdit }: {
lab: Laboratorio[]; lab: Laboratorio[];
patientId: string; patientId: string;
internacionId: string;
add: (l: Omit<Laboratorio, 'id'>) => void; add: (l: Omit<Laboratorio, 'id'>) => void;
update: (id: string, data: Partial<Laboratorio>) => void; update: (id: string, data: Partial<Laboratorio>) => void;
del: (id: string) => void; del: (id: string) => void;
@@ -938,13 +910,11 @@ function SeccionLaboratorios({ lab, patientId, add, update, del, addAcidoBase, c
const [dialog, setDialog] = useState(false); const [dialog, setDialog] = useState(false);
const [obsDialog, setObsDialog] = useState(false); const [obsDialog, setObsDialog] = useState(false);
const [evolDialog, setEvolDialog] = useState(false); const [evolDialog, setEvolDialog] = useState(false);
const [selectedLab, setSelectedLab] = useState<Laboratorio | null>(null);
const [edit, setEdit] = useState<Laboratorio | null>(null); const [edit, setEdit] = useState<Laboratorio | null>(null);
const [obsLab, setObsLab] = useState<Laboratorio | null>(null); const [obsLab, setObsLab] = useState<Laboratorio | null>(null);
const [fecha, setFecha] = useState(new Date().toISOString().split('T')[0]); const [fecha, setFecha] = useState(new Date().toISOString().split('T')[0]);
const [hora, setHora] = useState(new Date().toTimeString().slice(0, 5)); const [hora, setHora] = useState(new Date().toTimeString().slice(0, 5));
const [observaciones, setObservaciones] = useState(''); const [observaciones, setObservaciones] = useState('');
const [resultados, setResultados] = useState<ResultadoLaboratorio[]>([]);
const [hto, setHto] = useState(''); const [hto, setHto] = useState('');
const [hb, setHb] = useState(''); const [hb, setHb] = useState('');
const [gb, setGb] = useState(''); const [gb, setGb] = useState('');
@@ -988,13 +958,6 @@ function SeccionLaboratorios({ lab, patientId, add, update, del, addAcidoBase, c
return { fecha: l.fecha, valor: r ? parseFloat(String(r.valor)) : null }; return { fecha: l.fecha, valor: r ? parseFloat(String(r.valor)) : null };
}); });
const getEstadoColor = (estado: ResultadoLaboratorio['estado']) => {
switch (estado) {
case 'Normal': return 'bg-green-100 text-green-800';
case 'Alto': case 'Bajo': case 'Crítico': return 'bg-red-100 text-red-800';
}
};
const getValor = (l: Laboratorio, param: string) => { const getValor = (l: Laboratorio, param: string) => {
const r = l.resultados.find(r => r.parametro === param); const r = l.resultados.find(r => r.parametro === param);
return r ? `${r.valor}` : '-'; return r ? `${r.valor}` : '-';
@@ -1357,7 +1320,7 @@ function SeccionLaboratorios({ lab, patientId, add, update, del, addAcidoBase, c
if (importResultados.length === 0 && !importObservaciones && !importAcidoBase) return; if (importResultados.length === 0 && !importObservaciones && !importAcidoBase) return;
if (importAcidoBase) { if (importAcidoBase) {
addAcidoBase({ ...importAcidoBase, fecha: importFecha, hora: importHora }); addAcidoBase({ ...importAcidoBase, pacienteId: patientId, internacionId, fecha: importFecha, hora: importHora });
} }
add({ add({
@@ -1407,7 +1370,7 @@ function SeccionLaboratorios({ lab, patientId, add, update, del, addAcidoBase, c
if (edit) { if (edit) {
update(edit.id, { fecha, hora, resultados: resultadosLaboratorio, observaciones }); update(edit.id, { fecha, hora, resultados: resultadosLaboratorio, observaciones });
} else { } else {
add({ pacienteId: patientId || '', fecha, hora, resultados: resultadosLaboratorio, observaciones }); add({ pacienteId: patientId || '', internacionId, fecha, hora, resultados: resultadosLaboratorio, observaciones });
} }
setDialog(false); setDialog(false);
reset(); reset();
@@ -1761,7 +1724,6 @@ function SeccionEvoluciones({ evos, internacionId, add, update, del, canEdit }:
const [dialog, setDialog] = useState(false); const [dialog, setDialog] = useState(false);
const [edit, setEdit] = useState<Evolucion | null>(null); const [edit, setEdit] = useState<Evolucion | null>(null);
const [evoDetalle, setEvoDetalle] = useState<Evolucion | null>(null); const [evoDetalle, setEvoDetalle] = useState<Evolucion | null>(null);
const [expanded, setExpanded] = useState<string | null>(null);
const [fecha, setFecha] = useState(new Date().toISOString().split('T')[0]); const [fecha, setFecha] = useState(new Date().toISOString().split('T')[0]);
const [hora, setHora] = useState(new Date().toTimeString().slice(0, 5)); const [hora, setHora] = useState(new Date().toTimeString().slice(0, 5));
const effectiveMedico = getNombreProfesional(currentUser); const effectiveMedico = getNombreProfesional(currentUser);
@@ -2067,9 +2029,10 @@ function SeccionEvoluciones({ evos, internacionId, add, update, del, canEdit }:
); );
} }
function SeccionAcidosBase({ ab, patientId, add, update, del, canEdit }: { function SeccionAcidosBase({ ab, patientId, internacionId, add, update, del, canEdit }: {
ab: AcidoBase[]; ab: AcidoBase[];
patientId: string; patientId: string;
internacionId: string;
add: (a: Omit<AcidoBase, 'id'>) => void; add: (a: Omit<AcidoBase, 'id'>) => void;
update: (id: string, datos: Partial<AcidoBase>) => void; update: (id: string, datos: Partial<AcidoBase>) => void;
del: (id: string) => void; del: (id: string) => void;
@@ -2116,15 +2079,6 @@ function SeccionAcidosBase({ ab, patientId, add, update, del, canEdit }: {
const abFiltered = ab.filter(a => a.pacienteId === patientId).sort((a, b) => new Date(b.fecha + 'T' + b.hora).getTime() - new Date(a.fecha + 'T' + a.hora).getTime()); const abFiltered = ab.filter(a => a.pacienteId === patientId).sort((a, b) => new Date(b.fecha + 'T' + b.hora).getTime() - new Date(a.fecha + 'T' + a.hora).getTime());
const getEstadoParametro = (param: string, val: number) => {
const refs: Record<string, { min: number; max: number }> = { ph: { min: 7.35, max: 7.45 }, pco2: { min: 35, max: 45 }, po2: { min: 80, max: 100 }, hco3: { min: 22, max: 26 }, be: { min: -2, max: 2 }, sato2: { min: 95, max: 100 } };
const r = refs[param];
if (!r) return { estado: 'Normal', color: 'text-gray-800' };
if (val < r.min) return { estado: 'Bajo', color: 'text-blue-600' };
if (val > r.max) return { estado: 'Alto', color: 'text-red-600' };
return { estado: 'Normal', color: 'text-green-600' };
};
const interpretarGasometria = () => { const interpretarGasometria = () => {
const phVal = parseFloat(ph), pco2Val = parseFloat(pco2), hco3Val = parseFloat(hco3), beVal = parseFloat(be); const phVal = parseFloat(ph), pco2Val = parseFloat(pco2), hco3Val = parseFloat(hco3), beVal = parseFloat(be);
if (!phVal || !pco2Val || !hco3Val) return ''; if (!phVal || !pco2Val || !hco3Val) return '';
@@ -2136,16 +2090,10 @@ function SeccionAcidosBase({ ab, patientId, add, update, del, canEdit }: {
return interp; return interp;
}; };
const getColorPh = (ph: number) => {
if (ph < 7.2 || ph > 7.6) return 'bg-red-100 text-red-800';
if (ph < 7.35 || ph > 7.45) return 'bg-amber-100 text-amber-800';
return 'bg-green-100 text-green-800';
};
const handle = () => { const handle = () => {
if (!ph || !pco2 || !po2 || !hco3 || !be || !sato2) return; if (!ph || !pco2 || !po2 || !hco3 || !be || !sato2) return;
const interpretacionAuto = interpretarGasometria(); const interpretacionAuto = interpretarGasometria();
add({ pacienteId: patientId, fecha, hora, ph: parseFloat(ph), pco2: parseFloat(pco2), po2: parseFloat(po2), hco3: parseFloat(hco3), be: parseFloat(be), sato2: parseFloat(sato2), lactato: lactato ? parseFloat(lactato) : undefined, fio2: fio2 ? parseFloat(fio2) : undefined, interpretacion: interpretacion || interpretacionAuto }); add({ pacienteId: patientId, internacionId, fecha, hora, ph: parseFloat(ph), pco2: parseFloat(pco2), po2: parseFloat(po2), hco3: parseFloat(hco3), be: parseFloat(be), sato2: parseFloat(sato2), lactato: lactato ? parseFloat(lactato) : undefined, fio2: fio2 ? parseFloat(fio2) : undefined, interpretacion: interpretacion || interpretacionAuto });
setDialog(false); setDialog(false);
reset(); reset();
}; };
@@ -2300,9 +2248,10 @@ function SeccionAcidosBase({ ab, patientId, add, update, del, canEdit }: {
); );
} }
function SeccionCultivos({ cults, patient, add, update, del, canEdit }: { function SeccionCultivos({ cults, patient, internacionId, add, update, del, canEdit }: {
cults: Cultivo[]; cults: Cultivo[];
patient: Paciente; patient: Paciente;
internacionId: string;
add: (c: Omit<Cultivo, 'id'>) => void; add: (c: Omit<Cultivo, 'id'>) => void;
update: (id: string, data: Partial<Cultivo>) => void; update: (id: string, data: Partial<Cultivo>) => void;
del: (id: string) => void; del: (id: string) => void;
@@ -2312,7 +2261,6 @@ function SeccionCultivos({ cults, patient, add, update, del, canEdit }: {
const [resDialog, setResDialog] = useState(false); const [resDialog, setResDialog] = useState(false);
const [editDialog, setEditDialog] = useState(false); const [editDialog, setEditDialog] = useState(false);
const [selected, setSelected] = useState<Cultivo | null>(null); const [selected, setSelected] = useState<Cultivo | null>(null);
const [isParcialMode, setIsParcialMode] = useState(false);
const [fechaToma, setFechaToma] = useState(new Date().toISOString().split('T')[0]); const [fechaToma, setFechaToma] = useState(new Date().toISOString().split('T')[0]);
const [protocolo, setProtocolo] = useState(''); const [protocolo, setProtocolo] = useState('');
const [tipoMuestra, setTipoMuestra] = useState<Cultivo['tipoMuestra']>('HMCx2'); const [tipoMuestra, setTipoMuestra] = useState<Cultivo['tipoMuestra']>('HMCx2');
@@ -2340,29 +2288,8 @@ function SeccionCultivos({ cults, patient, add, update, del, canEdit }: {
setSelected(null); setSelected(null);
}; };
const abrirParcial = (c: Cultivo) => {
setSelected(c);
setProtocolo(c.protocolo || '');
setGermen(c.germen || '');
setSensible(c.sensible || '');
setResistente(c.resistente || '');
setResDialog(true);
};
const abrirDefinitivo = (c: Cultivo) => {
setSelected(c);
setProtocolo(c.protocolo || '');
setFechaResultado(c.fechaResultado || new Date().toISOString().split('T')[0]);
setEstadoResultado(c.estado === 'Parcial' || c.estado === 'Positivo' ? 'Positivo' : c.estado);
setGermen(c.germen || '');
setSensible(c.sensible || '');
setResistente(c.resistente || '');
setEditDialog(true);
};
const openParcial = (c: Cultivo) => { const openParcial = (c: Cultivo) => {
setSelected(c); setSelected(c);
setIsParcialMode(true);
setProtocolo(c.protocolo || ''); setProtocolo(c.protocolo || '');
setGermen(c.germen || ''); setGermen(c.germen || '');
setSensible(c.sensible || ''); setSensible(c.sensible || '');
@@ -2372,7 +2299,6 @@ function SeccionCultivos({ cults, patient, add, update, del, canEdit }: {
const openDefinitivo = (c: Cultivo) => { const openDefinitivo = (c: Cultivo) => {
setSelected(c); setSelected(c);
setIsParcialMode(false);
setProtocolo(c.protocolo || ''); setProtocolo(c.protocolo || '');
setFechaResultado(c.fechaResultado || new Date().toISOString().split('T')[0]); setFechaResultado(c.fechaResultado || new Date().toISOString().split('T')[0]);
setEstadoResultado(c.estado === 'Parcial' ? 'Positivo' : c.estado); setEstadoResultado(c.estado === 'Parcial' ? 'Positivo' : c.estado);
@@ -2402,7 +2328,7 @@ function SeccionCultivos({ cults, patient, add, update, del, canEdit }: {
} }
}; };
const handle = () => { add({ pacienteId: patient.id, fechaToma, protocolo, tipoMuestra, observaciones: observaciones || undefined, estado: 'NAF/Pendiente' }); setDialog(false); reset(); }; const handle = () => { add({ pacienteId: patient.id, internacionId, fechaToma, protocolo, tipoMuestra, observaciones: observaciones || undefined, estado: 'NAF/Pendiente' }); setDialog(false); reset(); };
const handleParcial = () => { const handleParcial = () => {
if (!selected) return; if (!selected) return;
@@ -2753,7 +2679,7 @@ function SeccionInterconsultas({ interconsultas, pacienteId, internacionId, add,
const handleGuardar = () => { const handleGuardar = () => {
if (!servicioInterconsultado) return; if (!servicioInterconsultado) return;
const datos: any = { fecha, servicioInterconsultado, motivo, respuestaInterconsulta }; const datos: Partial<Interconsulta> = { fecha, servicioInterconsultado, motivo, respuestaInterconsulta };
if (respuestaInterconsulta) { if (respuestaInterconsulta) {
datos.respuestaFecha = respuestaFecha || new Date().toISOString().split('T')[0]; datos.respuestaFecha = respuestaFecha || new Date().toISOString().split('T')[0];
} }
+45 -2
View File
@@ -1,5 +1,5 @@
import { useState } from 'react'; import { useState } from 'react';
import { ClipboardList, Plus, Search, LogOut, CheckCircle2, MoreHorizontal, FileText } from 'lucide-react'; import { ClipboardList, Plus, Search, LogOut, CheckCircle2, MoreHorizontal, FileText, Trash2 } from 'lucide-react';
import { Card, CardContent } from '@/components/ui/card'; import { Card, CardContent } from '@/components/ui/card';
import { Badge } from '@/components/ui/badge'; import { Badge } from '@/components/ui/badge';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
@@ -28,6 +28,7 @@ interface InternacionesProps {
diagnosticoEgreso: string; diagnosticoEgreso: string;
motivoEgreso: Internacion['motivoEgreso'] motivoEgreso: Internacion['motivoEgreso']
}) => void; }) => void;
onEliminarInternacion?: (internacionId: string) => Promise<void> | void;
getPacienteById: (id: string) => Paciente | undefined; getPacienteById: (id: string) => Paciente | undefined;
getCamaById: (id: string) => Cama | undefined; getCamaById: (id: string) => Cama | undefined;
onVerHC?: (internacionId: string) => void; onVerHC?: (internacionId: string) => void;
@@ -40,6 +41,7 @@ export function Internaciones({
camas, camas,
onIniciarInternacion, onIniciarInternacion,
onFinalizarInternacion, onFinalizarInternacion,
onEliminarInternacion,
getPacienteById, getPacienteById,
getCamaById, getCamaById,
grupos, grupos,
@@ -50,7 +52,9 @@ export function Internaciones({
const [filtroEstado, setFiltroEstado] = useState<'todas' | 'activas' | 'finalizadas'>('todas'); const [filtroEstado, setFiltroEstado] = useState<'todas' | 'activas' | 'finalizadas'>('todas');
const [dialogoNuevaAbierto, setDialogoNuevaAbierto] = useState(false); const [dialogoNuevaAbierto, setDialogoNuevaAbierto] = useState(false);
const [dialogoEgresoAbierto, setDialogoEgresoAbierto] = useState(false); const [dialogoEgresoAbierto, setDialogoEgresoAbierto] = useState(false);
const [dialogoEliminarAbierto, setDialogoEliminarAbierto] = useState(false);
const [internacionSeleccionada, setInternacionSeleccionada] = useState<Internacion | null>(null); const [internacionSeleccionada, setInternacionSeleccionada] = useState<Internacion | null>(null);
const [internacionAEliminar, setInternacionAEliminar] = useState<Internacion | null>(null);
const [busqueda, setBusqueda] = useState(''); const [busqueda, setBusqueda] = useState('');
const [pacienteSeleccionado, setPacienteSeleccionado] = useState<string>(''); const [pacienteSeleccionado, setPacienteSeleccionado] = useState<string>('');
const [camaSeleccionada, setCamaSeleccionada] = useState<string>(''); const [camaSeleccionada, setCamaSeleccionada] = useState<string>('');
@@ -488,6 +492,16 @@ export function Internaciones({
<FileText className="mr-2 h-4 w-4" /> <FileText className="mr-2 h-4 w-4" />
Ver HC Ver HC
</DropdownMenuItem> </DropdownMenuItem>
<DropdownMenuItem
className="text-red-600 focus:text-red-600"
onClick={() => {
setInternacionAEliminar(internacion);
setDialogoEliminarAbierto(true);
}}
>
<Trash2 className="mr-2 h-4 w-4" />
Eliminar HC
</DropdownMenuItem>
{internacion.activa && ( {internacion.activa && (
<DropdownMenuItem onClick={() => abrirDialogoEgreso(internacion)}> <DropdownMenuItem onClick={() => abrirDialogoEgreso(internacion)}>
<LogOut className="mr-2 h-4 w-4" /> <LogOut className="mr-2 h-4 w-4" />
@@ -567,7 +581,36 @@ export function Internaciones({
</div> </div>
</DialogContent> </DialogContent>
</Dialog> </Dialog>
{/* Historia Clínica ahora es una página separada */} {/* Modal Confirmar Eliminación HC */}
<Dialog open={dialogoEliminarAbierto} onOpenChange={(open) => { if (!open) setInternacionAEliminar(null); setDialogoEliminarAbierto(open); }}>
<DialogContent className="sm:max-w-md">
<DialogHeader>
<DialogTitle>Eliminar Historia Clínica de Internación</DialogTitle>
</DialogHeader>
<div className="space-y-4 py-2">
<p className="text-sm text-muted-foreground">
¿Está seguro de que desea eliminar esta historia clínica de internación y todos sus registros asociados (evoluciones, laboratorios, cultivos, indicaciones, etc.)? Esta acción no se puede deshacer.
</p>
<div className="flex justify-end gap-2 pt-2">
<Button variant="outline" onClick={() => setDialogoEliminarAbierto(false)}>
Cancelar
</Button>
<Button
variant="destructive"
onClick={async () => {
if (internacionAEliminar && typeof onEliminarInternacion === 'function') {
await onEliminarInternacion(internacionAEliminar.id);
setDialogoEliminarAbierto(false);
setInternacionAEliminar(null);
}
}}
>
Eliminar Definitivamente
</Button>
</div>
</div>
</DialogContent>
</Dialog>
</div> </div>
); );
} }
+18 -7
View File
@@ -1,3 +1,4 @@
import { useState } from 'react';
import { import {
LayoutDashboard, LayoutDashboard,
Bed, Bed,
@@ -35,6 +36,7 @@ interface LayoutProps {
export function Layout({ children, vistaActual, onCambiarVista, currentUser, onLogout }: LayoutProps) { export function Layout({ children, vistaActual, onCambiarVista, currentUser, onLogout }: LayoutProps) {
const { theme, setTheme } = useTheme() const { theme, setTheme } = useTheme()
const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
const toggleDarkMode = () => setTheme(theme === "dark" ? "light" : "dark") const toggleDarkMode = () => setTheme(theme === "dark" ? "light" : "dark")
@@ -59,7 +61,7 @@ export function Layout({ children, vistaActual, onCambiarVista, currentUser, onL
filteredMenuItems.push({ vista: 'usuarios', label: 'Usuarios', icon: UserCog }); filteredMenuItems.push({ vista: 'usuarios', label: 'Usuarios', icon: UserCog });
} }
const renderNavContent = () => ( const renderNavContent = (onItemClick?: () => void) => (
<nav className="flex flex-col gap-2"> <nav className="flex flex-col gap-2">
{filteredMenuItems.map((item) => { {filteredMenuItems.map((item) => {
const Icon = item.icon; const Icon = item.icon;
@@ -69,7 +71,10 @@ export function Layout({ children, vistaActual, onCambiarVista, currentUser, onL
key={item.vista} key={item.vista}
variant={isActive ? 'default' : 'ghost'} variant={isActive ? 'default' : 'ghost'}
className={`justify-start gap-3 ${isActive ? 'bg-blue-600 hover:bg-blue-700 text-white' : 'hover:bg-gray-100 dark:hover:bg-gray-700 dark:text-gray-200'}`} className={`justify-start gap-3 ${isActive ? 'bg-blue-600 hover:bg-blue-700 text-white' : 'hover:bg-gray-100 dark:hover:bg-gray-700 dark:text-gray-200'}`}
onClick={() => onCambiarVista(item.vista)} onClick={() => {
onCambiarVista(item.vista);
if (onItemClick) onItemClick();
}}
> >
<Icon className="h-5 w-5" /> <Icon className="h-5 w-5" />
<span>{item.label}</span> <span>{item.label}</span>
@@ -141,9 +146,9 @@ export function Layout({ children, vistaActual, onCambiarVista, currentUser, onL
</div> </div>
<span className="font-bold text-gray-900 dark:text-white">Gestion Historia Clinica Electronica</span> <span className="font-bold text-gray-900 dark:text-white">Gestion Historia Clinica Electronica</span>
</div> </div>
<Sheet> <Sheet open={mobileMenuOpen} onOpenChange={setMobileMenuOpen}>
<SheetTrigger asChild> <SheetTrigger asChild>
<Button variant="ghost" size="icon" className="dark:text-white"> <Button variant="ghost" size="icon" className="dark:text-white" onClick={() => setMobileMenuOpen(true)}>
<Menu className="h-6 w-6" /> <Menu className="h-6 w-6" />
</Button> </Button>
</SheetTrigger> </SheetTrigger>
@@ -162,13 +167,16 @@ export function Layout({ children, vistaActual, onCambiarVista, currentUser, onL
</div> </div>
</div> </div>
<div className="p-4"> <div className="p-4">
{renderNavContent()} {renderNavContent(() => setMobileMenuOpen(false))}
</div> </div>
<div className="p-4 border-t border-gray-200 dark:border-gray-700 space-y-2"> <div className="p-4 border-t border-gray-200 dark:border-gray-700 space-y-2">
<Button <Button
variant="ghost" variant="ghost"
size="sm" size="sm"
onClick={toggleDarkMode} onClick={() => {
toggleDarkMode();
setMobileMenuOpen(false);
}}
className="w-full flex items-center justify-center gap-2 dark:text-white" className="w-full flex items-center justify-center gap-2 dark:text-white"
> >
{theme === "dark" ? <Sun className="h-4 w-4" /> : <Moon className="h-4 w-4" />} {theme === "dark" ? <Sun className="h-4 w-4" /> : <Moon className="h-4 w-4" />}
@@ -177,7 +185,10 @@ export function Layout({ children, vistaActual, onCambiarVista, currentUser, onL
<Button <Button
variant="ghost" variant="ghost"
size="sm" size="sm"
onClick={onLogout} onClick={() => {
onLogout();
setMobileMenuOpen(false);
}}
className="w-full flex items-center justify-center gap-2 dark:text-white" className="w-full flex items-center justify-center gap-2 dark:text-white"
> >
<LogOut className="h-4 w-4" /> <LogOut className="h-4 w-4" />
+2 -2
View File
@@ -21,8 +21,8 @@ export function Login() {
try { try {
await login(dni, password); await login(dni, password);
window.location.reload(); window.location.reload();
} catch (err: any) { } catch (err) {
setError(err.message || 'Error de autenticación'); setError(err instanceof Error ? err.message : 'Error de autenticación');
} finally { } finally {
setLoading(false); setLoading(false);
} }
+4 -7
View File
@@ -18,10 +18,10 @@ interface MapaCamasProps {
pacientes: Paciente[]; pacientes: Paciente[];
internaciones: Internacion[]; internaciones: Internacion[];
onActualizarCama: (id: string, datos: Partial<Cama>) => void; onActualizarCama: (id: string, datos: Partial<Cama>) => void;
onAgregarCama: (cama: Omit<Cama, 'id'>) => any; onAgregarCama: (cama: Omit<Cama, 'id'>) => Promise<string | void>;
onEliminarCama: (id: string) => void; onEliminarCama: (id: string) => void;
onIniciarInternacion: (internacion: Omit<Internacion, 'id' | 'activa'>) => Promise<string>; onIniciarInternacion: (internacion: Omit<Internacion, 'id' | 'activa'>) => Promise<string>;
onAgregarGrupo: (grupo: Omit<Grupo, 'id'>) => any; onAgregarGrupo: (grupo: Omit<Grupo, 'id'>) => Promise<string | void>;
onActualizarGrupo: (id: string, datos: Partial<Grupo>) => void; onActualizarGrupo: (id: string, datos: Partial<Grupo>) => void;
onEliminarGrupo: (id: string) => void; onEliminarGrupo: (id: string) => void;
getPacienteById: (id: string) => Paciente | undefined; getPacienteById: (id: string) => Paciente | undefined;
@@ -41,9 +41,8 @@ export function MapaCamas({
onActualizarGrupo, onActualizarGrupo,
onEliminarGrupo, onEliminarGrupo,
getPacienteById, getPacienteById,
getInternacionById
}: MapaCamasProps) { }: MapaCamasProps) {
const { canEditCama, canAccessGrupo, currentUser } = useHospitalStore(); const { canEditCama, currentUser } = useHospitalStore();
const [filtroSala, setFiltroSala] = useState<string>('todas'); const [filtroSala, setFiltroSala] = useState<string>('todas');
const [filtroTipo, setFiltroTipo] = useState<string>('todos'); const [filtroTipo, setFiltroTipo] = useState<string>('todos');
const [filtroEstado, setFiltroEstado] = useState<string>('todos'); const [filtroEstado, setFiltroEstado] = useState<string>('todos');
@@ -479,9 +478,7 @@ export function MapaCamas({
)} )}
{canEditCama(cama.id) && ( {canEditCama(cama.id) && (
<Button variant="destructive" onClick={() => { <Button variant="destructive" onClick={() => {
if (confirm('Eliminar esta cama?')) {
onEliminarCama(cama.id); onEliminarCama(cama.id);
}
}}> }}>
<Trash className="h-4 w-4 mr-1" />Eliminar <Trash className="h-4 w-4 mr-1" />Eliminar
</Button> </Button>
@@ -589,7 +586,7 @@ export function MapaCamas({
</div> </div>
<div className="flex gap-2 justify-end"> <div className="flex gap-2 justify-end">
{editingBed && ( {editingBed && (
<Button variant="destructive" onClick={() => { if (confirm('Eliminar cama?')) { onEliminarCama(editingBed.id); setBedDialogOpen(false); } }}> <Button variant="destructive" onClick={() => { onEliminarCama(editingBed.id); setBedDialogOpen(false); }}>
Eliminar Eliminar
</Button> </Button>
)} )}
+6 -2
View File
@@ -21,9 +21,10 @@ interface NuevoIngresoProps {
onActualizarPaciente?: (id: string, datos: Partial<Paciente>) => void; onActualizarPaciente?: (id: string, datos: Partial<Paciente>) => void;
onVolver: () => void; onVolver: () => void;
getGrupoName: (grupoId: string | undefined) => string; getGrupoName: (grupoId: string | undefined) => string;
internaciones: Internacion[];
} }
export function NuevoIngreso({ pacientes, camas, grupos, onIniciarInternacion, onAgregarCama, onVolver, getGrupoName }: NuevoIngresoProps) { export function NuevoIngreso({ pacientes, camas, grupos, onIniciarInternacion, onAgregarCama, onVolver, getGrupoName, internaciones }: NuevoIngresoProps) {
const { currentUser } = useHospitalStore(); const { currentUser } = useHospitalStore();
const [pacienteSeleccionado, setPacienteSeleccionado] = useState<string>(''); const [pacienteSeleccionado, setPacienteSeleccionado] = useState<string>('');
const [busqueda, setBusqueda] = useState(''); const [busqueda, setBusqueda] = useState('');
@@ -61,7 +62,10 @@ export function NuevoIngreso({ pacientes, camas, grupos, onIniciarInternacion, o
const grupoFueraDeGrupo = grupos.find(a => normalizeStr(a.nombre) === 'fuera de grupo'); const grupoFueraDeGrupo = grupos.find(a => normalizeStr(a.nombre) === 'fuera de grupo');
const grupoFueraDeGrupoId = grupoFueraDeGrupo?.id || grupos[0]?.id || 'fuera-de-grupo'; const grupoFueraDeGrupoId = grupoFueraDeGrupo?.id || grupos[0]?.id || 'fuera-de-grupo';
const pacientesSinInternar = pacientes; const pacientesSinInternar = pacientes.filter(p => {
const internacionActiva = internaciones.find(i => i.pacienteId === p.id && i.activa);
return !internacionActiva;
});
const parseBedNumber = (numero: string) => { const parseBedNumber = (numero: string) => {
const match = numero.match(/^(\d{3})-(\d+)$/); const match = numero.match(/^(\d{3})-(\d+)$/);
+11
View File
@@ -225,6 +225,17 @@ export interface Indicacion {
fechaSuspension?: string; fechaSuspension?: string;
} }
export interface MovimientoIndicacion {
id: string;
internacionId: string;
indicacionId?: string;
tipo: string;
fecha: string;
hora: string;
medico: string;
observaciones?: string;
}
export type Vista = 'dashboard' | 'camas' | 'pacientes' | 'internaciones' | 'evoluciones' | 'laboratorios' | 'acidobase' | 'cultivos' | 'historiaclinica' | 'nuevoingreso' | 'editaringreso' | 'usuarios'; export type Vista = 'dashboard' | 'camas' | 'pacientes' | 'internaciones' | 'evoluciones' | 'laboratorios' | 'acidobase' | 'cultivos' | 'historiaclinica' | 'nuevoingreso' | 'editaringreso' | 'usuarios';
export type RolUsuario = 'admin' | 'medico' | 'enfermero'; export type RolUsuario = 'admin' | 'medico' | 'enfermero';