feat: update hospital management system and fix linter issues
This commit is contained in:
+11
-9
@@ -17,6 +17,7 @@ import { Spinner } from '@/components/ui/spinner';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { ThemeProvider } from "@/components/theme-provider";
|
||||
import { Toaster } from '@/components/ui/sonner';
|
||||
import type { Indicacion, MovimientoIndicacion } from '@/types';
|
||||
|
||||
function AppContent() {
|
||||
const store = useHospitalStore();
|
||||
@@ -53,7 +54,7 @@ function AppContent() {
|
||||
pacientes={store.pacientes}
|
||||
internaciones={store.internaciones}
|
||||
onActualizarCama={store.actualizarCama}
|
||||
onAgregarCama={store.agregarCama as any}
|
||||
onAgregarCama={store.agregarCama}
|
||||
onEliminarCama={store.eliminarCama}
|
||||
onIniciarInternacion={store.iniciarInternacion}
|
||||
getPacienteById={store.getPacienteById}
|
||||
@@ -85,6 +86,7 @@ function AppContent() {
|
||||
cultivos={store.cultivos}
|
||||
onIniciarInternacion={store.iniciarInternacion}
|
||||
onFinalizarInternacion={store.finalizarInternacion}
|
||||
onEliminarInternacion={store.eliminarInternacion}
|
||||
getPacienteById={store.getPacienteById}
|
||||
getCamaById={store.getCamaById}
|
||||
onVerHC={(id) => { store.setCurrentInternacion(id); store.setVista('historiaclinica'); }}
|
||||
@@ -134,7 +136,6 @@ function AppContent() {
|
||||
case 'historiaclinica': {
|
||||
const internacionId = store.currentInternacionId || '';
|
||||
const internacion = store.getInternacionById(internacionId);
|
||||
const { datosVitales, evoluciones, laboratorios, acidosBase, cultivos, estudios, interconsultas, atb, indicaciones } = store;
|
||||
const paciente = internacion ? store.getPacienteById(internacion.pacienteId) : undefined;
|
||||
if (!internacion || !paciente) {
|
||||
return (
|
||||
@@ -151,11 +152,11 @@ function AppContent() {
|
||||
cama={store.getCamaById(internacion.camaId)}
|
||||
allCamas={store.camas}
|
||||
evoluciones={store.getEvolucionesByInternacion(internacion.id)}
|
||||
laboratorios={store.getLaboratoriosByPaciente(paciente.id)}
|
||||
glucemias={store.getGlucemiasByPaciente(paciente.id)}
|
||||
acidosBase={store.getAcidosBaseByPaciente(paciente.id)}
|
||||
cultivos={store.getCultivosByPaciente(paciente.id)}
|
||||
estudiosComplementarios={store.estudiosComplementarios.filter(e => e.pacienteId === paciente.id)}
|
||||
laboratorios={store.getLaboratoriosByInternacion(internacion.id)}
|
||||
glucemias={store.getGlucemiasByInternacion(internacion.id)}
|
||||
acidosBase={store.getAcidosBaseByInternacion(internacion.id)}
|
||||
cultivos={store.getCultivosByInternacion(internacion.id)}
|
||||
estudiosComplementarios={store.estudiosComplementarios.filter(e => e.internacionId === internacion.id)}
|
||||
interconsultas={(store.interconsultas || []).filter(ic => ic.internacionId === internacion.id)}
|
||||
atb={(store.atb || []).filter(a => a.internacionId === internacion.id)}
|
||||
onAgregarEvolucion={store.agregarEvolucion}
|
||||
@@ -182,12 +183,12 @@ function AppContent() {
|
||||
onAgregarATB={(atb) => store.agregarATB({ ...atb, internacionId: internacion.id })}
|
||||
onActualizarATB={store.actualizarATB}
|
||||
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) : []}
|
||||
onAgregarIndicacion={(i) => store.agregarIndicacion({ ...i, internacionId: internacion.id })}
|
||||
onActualizarIndicacion={store.actualizarIndicacion}
|
||||
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}
|
||||
onActualizarCama={store.actualizarCama}
|
||||
onVolver={() => store.setVista('internaciones')}
|
||||
@@ -212,6 +213,7 @@ function AppContent() {
|
||||
onAgregarCama={store.agregarCama}
|
||||
onVolver={() => store.setVista('internaciones')}
|
||||
getGrupoName={(grupoId) => store.grupos.find(a => a.id === grupoId)?.nombre || ''}
|
||||
internaciones={store.internaciones}
|
||||
/>
|
||||
);
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable react-refresh/only-export-components */
|
||||
import { createContext, useContext, useEffect, useState } from "react"
|
||||
|
||||
type Theme = "dark" | "light" | "system"
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable react-refresh/only-export-components */
|
||||
import * as React from "react"
|
||||
import { Slot } from "@radix-ui/react-slot"
|
||||
import { cva, type VariantProps } from "class-variance-authority"
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable react-refresh/only-export-components */
|
||||
import { Slot } from "@radix-ui/react-slot"
|
||||
import { cva, type VariantProps } from "class-variance-authority"
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable react-refresh/only-export-components */
|
||||
import * as React from "react"
|
||||
import { Slot } from "@radix-ui/react-slot"
|
||||
import { cva, type VariantProps } from "class-variance-authority"
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
"use client"
|
||||
|
||||
/* eslint-disable react-refresh/only-export-components */
|
||||
import * as React from "react"
|
||||
import type * as LabelPrimitive from "@radix-ui/react-label"
|
||||
import { Slot } from "@radix-ui/react-slot"
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable react-refresh/only-export-components */
|
||||
import * as React from "react"
|
||||
import * as NavigationMenuPrimitive from "@radix-ui/react-navigation-menu"
|
||||
import { cva } from "class-variance-authority"
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
"use client"
|
||||
|
||||
/* eslint-disable react-refresh/only-export-components */
|
||||
import * as React from "react"
|
||||
import { Slot } from "@radix-ui/react-slot"
|
||||
import { cva, type VariantProps } from "class-variance-authority"
|
||||
@@ -607,9 +608,7 @@ function SidebarMenuSkeleton({
|
||||
showIcon?: boolean
|
||||
}) {
|
||||
// Random width between 50 to 90%.
|
||||
const width = React.useMemo(() => {
|
||||
return `${Math.floor(Math.random() * 40) + 50}%`
|
||||
}, [])
|
||||
const width = "70%";
|
||||
|
||||
return (
|
||||
<div
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable react-refresh/only-export-components */
|
||||
import * as React from "react"
|
||||
import * as TogglePrimitive from "@radix-ui/react-toggle"
|
||||
import { cva, type VariantProps } from "class-variance-authority"
|
||||
|
||||
+146
-95
@@ -15,9 +15,9 @@ import type {
|
||||
Interconsulta,
|
||||
ATB,
|
||||
Indicacion,
|
||||
MovimientoIndicacion,
|
||||
Vista,
|
||||
Usuario,
|
||||
RolUsuario
|
||||
Usuario
|
||||
} from '@/types';
|
||||
|
||||
// Fallback UUID generator for non-secure contexts (HTTP without localhost)
|
||||
@@ -47,7 +47,7 @@ interface HospitalState {
|
||||
interconsultas: Interconsulta[];
|
||||
atb: ATB[];
|
||||
indicaciones: Indicacion[];
|
||||
movimientosIndicaciones: any[];
|
||||
movimientosIndicaciones: MovimientoIndicacion[];
|
||||
vistaActual: Vista;
|
||||
currentInternacionId?: string | null;
|
||||
usuarios: Usuario[];
|
||||
@@ -108,7 +108,7 @@ export function useHospitalStore() {
|
||||
};
|
||||
setState(normalized);
|
||||
}
|
||||
} catch (err) {
|
||||
} catch {
|
||||
// no remote state or unreachable — keep defaults
|
||||
} finally {
|
||||
if (mounted) setIsLoaded(true);
|
||||
@@ -118,7 +118,7 @@ export function useHospitalStore() {
|
||||
}, []);
|
||||
|
||||
// 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 {
|
||||
const res = await fetch(`${API_BASE}${endpoint}`, {
|
||||
method,
|
||||
@@ -133,11 +133,6 @@ export function useHospitalStore() {
|
||||
}
|
||||
}, []);
|
||||
|
||||
// Also save on specific events (optional)
|
||||
const queueSave = (keys: string[]) => {
|
||||
// Reserved for future implementation
|
||||
};
|
||||
|
||||
// Utility functions
|
||||
const getCamaGrupoId = useCallback((camaId: string): string | null => {
|
||||
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, '');
|
||||
};
|
||||
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]);
|
||||
}, [state.internaciones, state.currentUser, getInternacionGrupoId, canAccessGrupo]);
|
||||
|
||||
const canEditCama = useCallback((camaId: string): boolean => {
|
||||
const grupoId = getCamaGrupoId(camaId);
|
||||
@@ -303,7 +302,7 @@ export function useHospitalStore() {
|
||||
console.error('Error al actualizar cama:', err);
|
||||
throw err;
|
||||
}
|
||||
}, [state, canAccessGrupo, apiCall]);
|
||||
}, [state, apiCall]);
|
||||
|
||||
const agregarCama = useCallback(async (cama: Omit<Cama, 'id'>) => {
|
||||
const nuevaCama: Cama = {
|
||||
@@ -348,15 +347,18 @@ const agregarCama = useCallback(async (cama: Omit<Cama, 'id'>) => {
|
||||
id: generateUUID(),
|
||||
activa: true,
|
||||
};
|
||||
|
||||
try {
|
||||
try {
|
||||
// API call inmediata para crear internación
|
||||
await apiCall('POST', '/internaciones', nuevaInternacion);
|
||||
|
||||
// Actualizar estado de la cama en la base de datos (solo estado)
|
||||
await apiCall('PUT', `/camas/${internacion.camaId}`, {
|
||||
estado: 'Ocupada'
|
||||
});
|
||||
// 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 => ({
|
||||
@@ -364,7 +366,7 @@ const agregarCama = useCallback(async (cama: Omit<Cama, 'id'>) => {
|
||||
internaciones: [...prev.internaciones, nuevaInternacion],
|
||||
camas: prev.camas.map(c =>
|
||||
c.id === internacion.camaId
|
||||
? { ...c, estado: 'Ocupada' as const }
|
||||
? { ...c, estado: 'Ocupada' as const, pacienteId: internacion.pacienteId, internacionId: nuevaInternacion.id }
|
||||
: c
|
||||
),
|
||||
}));
|
||||
@@ -374,9 +376,9 @@ const agregarCama = useCallback(async (cama: Omit<Cama, 'id'>) => {
|
||||
console.error('Error al crear internación:', err);
|
||||
throw err;
|
||||
}
|
||||
}, [apiCall]);
|
||||
}, [apiCall, checkGrupoPermission]);
|
||||
|
||||
const finalizarInternacion = useCallback(async (internacionId: string, datos: {
|
||||
const finalizarInternacion = useCallback(async (internacionId: string, datos: {
|
||||
fechaEgreso: string;
|
||||
diagnosticoEgreso: string;
|
||||
motivoEgreso: Internacion['motivoEgreso']
|
||||
@@ -401,7 +403,9 @@ const finalizarInternacion = useCallback(async (internacionId: string, datos: {
|
||||
} else {
|
||||
// Marcar como disponible si es un área normal
|
||||
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
|
||||
),
|
||||
cams: esFueraDeGrupo
|
||||
camas: esFueraDeGrupo
|
||||
? prev.camas.filter(c => c.id !== camaId)
|
||||
: prev.camas.map(c =>
|
||||
c.id === camaId
|
||||
? { ...c, estado: 'Disponible' as const }
|
||||
? { ...c, estado: 'Disponible' as const, pacienteId: undefined, internacionId: undefined }
|
||||
: c
|
||||
),
|
||||
};
|
||||
@@ -430,20 +434,70 @@ const finalizarInternacion = useCallback(async (internacionId: string, datos: {
|
||||
}
|
||||
}, [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 internacion = state.internaciones.find(i => i.id === internacionId);
|
||||
if (!internacion) return;
|
||||
|
||||
// Check creator permission first
|
||||
const normalizeMedicoName = (name?: string) => {
|
||||
return (name || '').trim().toLowerCase().normalize('NFD').replace(/[\u0300-\u036f]/g, '');
|
||||
};
|
||||
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.`);
|
||||
// Check permissions
|
||||
if (!canEditIngreso(internacionId)) {
|
||||
toast.error(`No tiene permisos para modificar este ingreso.`);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -485,7 +539,7 @@ const actualizarInternacion = useCallback(async (internacionId: string, datos: P
|
||||
? { ...i, ...datos }
|
||||
: i
|
||||
),
|
||||
cams: prev.camas.map(c => {
|
||||
camas: prev.camas.map(c => {
|
||||
if (c.id === newCamaId && newCamaId !== oldCamaId) {
|
||||
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);
|
||||
throw err;
|
||||
}
|
||||
}, [state, canAccessGrupo, checkGrupoPermission, apiCall]);
|
||||
}, [state, canChangeBed, canEditIngreso, checkGrupoPermission, apiCall]);
|
||||
|
||||
// Acciones de evoluciones
|
||||
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);
|
||||
throw err;
|
||||
}
|
||||
}, [state, canAccessGrupo, checkGrupoPermission, apiCall]);
|
||||
}, [state, checkGrupoPermission, apiCall]);
|
||||
|
||||
const eliminarEvolucion = useCallback(async (id: string) => {
|
||||
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);
|
||||
throw err;
|
||||
}
|
||||
}, [state, canAccessGrupo, checkGrupoPermission, apiCall]);
|
||||
}, [state, checkGrupoPermission, apiCall]);
|
||||
|
||||
const actualizarEvolucion = useCallback(async (id: string, datos: Partial<Evolucion>) => {
|
||||
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);
|
||||
throw err;
|
||||
}
|
||||
}, [state, canAccessGrupo, checkGrupoPermission, apiCall]);
|
||||
}, [state, checkGrupoPermission, apiCall]);
|
||||
|
||||
// Acciones de laboratorios
|
||||
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);
|
||||
throw err;
|
||||
}
|
||||
}, [state, canAccessGrupo, checkGrupoPermission, apiCall]);
|
||||
}, [state, checkGrupoPermission, apiCall]);
|
||||
|
||||
const eliminarLaboratorio = useCallback(async (id: string) => {
|
||||
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);
|
||||
throw err;
|
||||
}
|
||||
}, [state, canAccessGrupo, checkGrupoPermission, apiCall]);
|
||||
}, [state, checkGrupoPermission, apiCall]);
|
||||
|
||||
const actualizarLaboratorio = useCallback(async (id: string, datos: Partial<Laboratorio>) => {
|
||||
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);
|
||||
throw err;
|
||||
}
|
||||
}, [state, canAccessGrupo, checkGrupoPermission, apiCall]);
|
||||
}, [state, checkGrupoPermission, apiCall]);
|
||||
|
||||
// Acciones de glucemias
|
||||
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);
|
||||
throw err;
|
||||
}
|
||||
}, [state, canAccessGrupo, checkGrupoPermission, apiCall]);
|
||||
}, [state, checkGrupoPermission, apiCall]);
|
||||
|
||||
const eliminarGlucemia = useCallback(async (id: string) => {
|
||||
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);
|
||||
throw err;
|
||||
}
|
||||
}, [state, canAccessGrupo, checkGrupoPermission, apiCall]);
|
||||
}, [state, checkGrupoPermission, apiCall]);
|
||||
|
||||
const actualizarGlucemia = useCallback(async (id: string, datos: Partial<Glucemia>) => {
|
||||
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);
|
||||
throw err;
|
||||
}
|
||||
}, [state, canAccessGrupo, checkGrupoPermission, apiCall]);
|
||||
}, [state, checkGrupoPermission, apiCall]);
|
||||
|
||||
// Acciones de ácido-base
|
||||
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);
|
||||
throw err;
|
||||
}
|
||||
}, [state, canAccessGrupo, checkGrupoPermission, apiCall]);
|
||||
}, [state, checkGrupoPermission, apiCall]);
|
||||
|
||||
const actualizarAcidoBase = useCallback(async (id: string, datos: Partial<AcidoBase>) => {
|
||||
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);
|
||||
throw err;
|
||||
}
|
||||
}, [state, canAccessGrupo, checkGrupoPermission, apiCall]);
|
||||
}, [state, checkGrupoPermission, apiCall]);
|
||||
|
||||
const eliminarAcidoBase = useCallback(async (id: string) => {
|
||||
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);
|
||||
throw err;
|
||||
}
|
||||
}, [state, canAccessGrupo, checkGrupoPermission, apiCall]);
|
||||
}, [state, checkGrupoPermission, apiCall]);
|
||||
|
||||
// Acciones de cultivos
|
||||
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);
|
||||
throw err;
|
||||
}
|
||||
}, [state, canAccessGrupo, checkGrupoPermission, apiCall]);
|
||||
}, [state, checkGrupoPermission, apiCall]);
|
||||
|
||||
const actualizarCultivo = useCallback(async (id: string, datos: Partial<Cultivo>) => {
|
||||
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);
|
||||
throw err;
|
||||
}
|
||||
}, [state, canAccessGrupo, checkGrupoPermission, apiCall]);
|
||||
}, [state, checkGrupoPermission, apiCall]);
|
||||
|
||||
const eliminarCultivo = useCallback(async (id: string) => {
|
||||
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);
|
||||
throw err;
|
||||
}
|
||||
}, [state, canAccessGrupo, checkGrupoPermission, apiCall]);
|
||||
}, [state, checkGrupoPermission, apiCall]);
|
||||
|
||||
// Acciones de estudios complementarios
|
||||
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);
|
||||
throw err;
|
||||
}
|
||||
}, [state, canAccessGrupo, checkGrupoPermission, apiCall]);
|
||||
}, [state, checkGrupoPermission, apiCall]);
|
||||
|
||||
const actualizarEstudioComplementario = useCallback(async (id: string, datos: Partial<EstudioComplementario>) => {
|
||||
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);
|
||||
throw err;
|
||||
}
|
||||
}, [state, canAccessGrupo, checkGrupoPermission, apiCall]);
|
||||
}, [state, checkGrupoPermission, apiCall]);
|
||||
|
||||
const eliminarEstudioComplementario = useCallback(async (id: string) => {
|
||||
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);
|
||||
throw err;
|
||||
}
|
||||
}, [state, canAccessGrupo, checkGrupoPermission, apiCall]);
|
||||
}, [state, checkGrupoPermission, apiCall]);
|
||||
|
||||
// Acciones de interconsultas
|
||||
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);
|
||||
throw err;
|
||||
}
|
||||
}, [state, canAccessGrupo, checkGrupoPermission, apiCall]);
|
||||
}, [state, checkGrupoPermission, apiCall]);
|
||||
|
||||
const actualizarInterconsulta = useCallback(async (id: string, datos: Partial<Interconsulta>) => {
|
||||
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);
|
||||
throw err;
|
||||
}
|
||||
}, [state, canAccessGrupo, checkGrupoPermission, apiCall]);
|
||||
}, [state, checkGrupoPermission, apiCall]);
|
||||
|
||||
const eliminarInterconsulta = useCallback(async (id: string) => {
|
||||
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);
|
||||
throw err;
|
||||
}
|
||||
}, [state, canAccessGrupo, checkGrupoPermission, apiCall]);
|
||||
}, [state, checkGrupoPermission, apiCall]);
|
||||
|
||||
const agregarATB = useCallback(async (atb: Omit<ATB, 'id'>) => {
|
||||
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);
|
||||
throw err;
|
||||
}
|
||||
}, [state, canAccessGrupo, checkGrupoPermission, apiCall]);
|
||||
}, [state, checkGrupoPermission, apiCall]);
|
||||
|
||||
const actualizarATB = useCallback(async (id: string, datos: Partial<ATB>) => {
|
||||
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);
|
||||
throw err;
|
||||
}
|
||||
}, [state, canAccessGrupo, checkGrupoPermission, apiCall]);
|
||||
}, [state, checkGrupoPermission, apiCall]);
|
||||
|
||||
const eliminarATB = useCallback(async (id: string) => {
|
||||
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);
|
||||
throw err;
|
||||
}
|
||||
}, [state, canAccessGrupo, checkGrupoPermission, apiCall]);
|
||||
}, [state, checkGrupoPermission, apiCall]);
|
||||
|
||||
const agregarIndicacion = useCallback(async (indicacion: Omit<Indicacion, 'id'>) => {
|
||||
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);
|
||||
throw err;
|
||||
}
|
||||
}, [state.internaciones, canAccessGrupo, checkGrupoPermission, apiCall]);
|
||||
}, [state.internaciones, checkGrupoPermission, apiCall]);
|
||||
|
||||
const actualizarIndicacion = useCallback(async (id: string, datos: Partial<Indicacion>) => {
|
||||
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);
|
||||
throw err;
|
||||
}
|
||||
}, [state.indicaciones, state.internaciones, canAccessGrupo, checkGrupoPermission, apiCall]);
|
||||
}, [state.indicaciones, state.internaciones, checkGrupoPermission, apiCall]);
|
||||
|
||||
const eliminarIndicacion = useCallback(async (id: string) => {
|
||||
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);
|
||||
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 grupoId = internacion?.grupoId || null;
|
||||
if (!checkGrupoPermission(grupoId)) {
|
||||
@@ -1116,12 +1170,12 @@ const actualizarInternacion = useCallback(async (internacionId: string, datos: P
|
||||
console.error('Error al agregar movimiento:', err);
|
||||
throw err;
|
||||
}
|
||||
}, [state, canAccessGrupo, checkGrupoPermission, apiCall]);
|
||||
}, [state, checkGrupoPermission, apiCall]);
|
||||
|
||||
const getMovimientosByInternacion = useCallback((internacionId: string) => {
|
||||
return (state.movimientosIndicaciones || [])
|
||||
.filter((m: any) => m.internacionId === internacionId)
|
||||
.sort((a: any, b: any) => new Date(b.fecha).getTime() - new Date(a.fecha).getTime());
|
||||
.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
|
||||
@@ -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());
|
||||
}, [state.evoluciones]);
|
||||
|
||||
const getLaboratoriosByPaciente = useCallback((pacienteId: string) => {
|
||||
const getLaboratoriosByInternacion = useCallback((internacionId: string) => {
|
||||
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());
|
||||
}, [state.laboratorios]);
|
||||
|
||||
const getGlucemiasByPaciente = useCallback((pacienteId: string) => {
|
||||
const getGlucemiasByInternacion = useCallback((internacionId: string) => {
|
||||
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());
|
||||
}, [state.glucemias]);
|
||||
|
||||
const getAcidosBaseByPaciente = useCallback((pacienteId: string) => {
|
||||
const getAcidosBaseByInternacion = useCallback((internacionId: string) => {
|
||||
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());
|
||||
}, [state.acidosBase]);
|
||||
|
||||
const getCultivosByPaciente = useCallback((pacienteId: string) => {
|
||||
const getCultivosByInternacion = useCallback((internacionId: string) => {
|
||||
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());
|
||||
}, [state.cultivos]);
|
||||
|
||||
@@ -1247,23 +1301,19 @@ const actualizarInternacion = useCallback(async (internacionId: string, datos: P
|
||||
|
||||
// Auth functions
|
||||
const login = useCallback(async (dni: string, password: string) => {
|
||||
try {
|
||||
const res = await fetch(`${API_BASE}/auth/login`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ dni, password }),
|
||||
});
|
||||
if (!res.ok) {
|
||||
const err = await res.json();
|
||||
throw new Error(err.error || 'Error en autenticación');
|
||||
}
|
||||
const user = await res.json();
|
||||
sessionStorage.setItem('hospital_user', JSON.stringify(user));
|
||||
setState(prev => ({ ...prev, currentUser: user, isAuthenticated: true }));
|
||||
return user;
|
||||
} catch (err) {
|
||||
throw err;
|
||||
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(() => {
|
||||
@@ -1311,6 +1361,7 @@ const actualizarInternacion = useCallback(async (internacionId: string, datos: P
|
||||
iniciarInternacion,
|
||||
finalizarInternacion,
|
||||
actualizarInternacion,
|
||||
eliminarInternacion,
|
||||
agregarCama,
|
||||
eliminarCama,
|
||||
agregarEvolucion,
|
||||
@@ -1350,10 +1401,10 @@ const actualizarInternacion = useCallback(async (internacionId: string, datos: P
|
||||
getInternacionById,
|
||||
getInternacionActivaByPaciente,
|
||||
getEvolucionesByInternacion,
|
||||
getLaboratoriosByPaciente,
|
||||
getGlucemiasByPaciente,
|
||||
getAcidosBaseByPaciente,
|
||||
getCultivosByPaciente,
|
||||
getLaboratoriosByInternacion,
|
||||
getGlucemiasByInternacion,
|
||||
getAcidosBaseByInternacion,
|
||||
getCultivosByInternacion,
|
||||
setCurrentInternacion,
|
||||
getEstadisticas,
|
||||
login,
|
||||
|
||||
+1
-2
@@ -40,8 +40,7 @@ export function calcularEdad(fechaNacimiento?: string): string | number {
|
||||
|
||||
|
||||
export function isCamaFueraDeGrupo(
|
||||
cama: { numero: string; grupoId?: string; areaId?: string; sector?: string },
|
||||
_gruposOrAreas?: { id: string; nombre: string }[]
|
||||
cama: { numero: string; grupoId?: string; areaId?: string; sector?: string }
|
||||
): boolean {
|
||||
if (cama.sector === 'Fuera de Área') return true;
|
||||
if (cama.sector === 'En Área') return false;
|
||||
|
||||
@@ -181,10 +181,6 @@ export function Cultivos({
|
||||
setDialogoDefinitivoAbierto(false);
|
||||
};
|
||||
|
||||
const getNumeroCama = (pacienteId: string) => {
|
||||
return '';
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<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"
|
||||
/>
|
||||
</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]">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
@@ -410,7 +406,7 @@ export function Cultivos({
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<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>
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
@@ -485,7 +481,7 @@ export function Cultivos({
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label>Estado</Label>
|
||||
<Select value={estadoResultado} onValueChange={(v) => setEstadoResultado(v as any)}>
|
||||
<Select value={estadoResultado} onValueChange={(v) => setEstadoResultado(v as Cultivo['estado'])}>
|
||||
<SelectTrigger>
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
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 { Button } from '@/components/ui/button';
|
||||
import { Input } from '@/components/ui/input';
|
||||
@@ -32,12 +32,14 @@ export function EditIngreso({
|
||||
onActualizarInternacion,
|
||||
onAgregarCama,
|
||||
onVolver,
|
||||
getGrupoName,
|
||||
}: EditIngresoProps) {
|
||||
const { currentUser } = useHospitalStore();
|
||||
const [pacienteSeleccionado] = useState(internacion.pacienteId);
|
||||
const [camaSeleccionada] = useState(internacion.camaId);
|
||||
const [camaSeleccionada, setCamaSeleccionada] = useState(internacion.camaId);
|
||||
const [grupoSeleccionada] = useState(internacion.grupoId);
|
||||
const [camaInput, setCamaInput] = useState('');
|
||||
const [busqueda, setBusqueda] = useState('');
|
||||
const [modoCama, setModoCama] = useState<'seleccionar' | 'escribir'>('seleccionar');
|
||||
const effectiveMedico = getNombreProfesional(currentUser);
|
||||
const [fechaIngresoHospital, setFechaIngresoHospital] = useState(internacion.fechaIngresoHospital || '');
|
||||
|
||||
@@ -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());
|
||||
|
||||
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 (
|
||||
<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">
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { useState } from 'react';
|
||||
import { toast } from 'sonner';
|
||||
import { PDFDocument } from 'pdf-lib';
|
||||
import { User } from 'lucide-react';
|
||||
import { useHospitalStore } from '@/hooks/useHospitalStore';
|
||||
import { getNombreProfesional } from '@/lib/utils';
|
||||
@@ -17,15 +16,12 @@ import {
|
||||
Plus,
|
||||
Calendar,
|
||||
Droplet,
|
||||
Clock,
|
||||
AlertCircle,
|
||||
CheckCircle2,
|
||||
ArrowLeft,
|
||||
Pencil,
|
||||
Trash2,
|
||||
FileText,
|
||||
ChevronDown,
|
||||
ChevronUp,
|
||||
List,
|
||||
FileDown,
|
||||
Thermometer,
|
||||
@@ -39,7 +35,6 @@ import {
|
||||
Edit,
|
||||
ClipboardList,
|
||||
TrendingUp,
|
||||
TypeOutline,
|
||||
Activity,
|
||||
Pill,
|
||||
Users
|
||||
@@ -49,14 +44,14 @@ import { Badge } from '@/components/ui/badge';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Input } from '@/components/ui/input';
|
||||
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 { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
|
||||
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table';
|
||||
import { ScrollArea, ScrollBar } from "@/components/ui/scroll-area"
|
||||
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from '@/components/ui/dropdown-menu';
|
||||
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 { SeccionIndicaciones } from './SeccionIndicaciones';
|
||||
|
||||
@@ -64,7 +59,6 @@ interface HistoriaClinicaProps {
|
||||
internacion: Internacion;
|
||||
paciente: Paciente;
|
||||
cama?: Cama;
|
||||
allCamas?: Cama[];
|
||||
evoluciones: Evolucion[];
|
||||
laboratorios: Laboratorio[];
|
||||
glucemias: Glucemia[];
|
||||
@@ -74,7 +68,7 @@ interface HistoriaClinicaProps {
|
||||
interconsultas: Interconsulta[];
|
||||
atb: ATB[];
|
||||
indicadores: Indicacion[];
|
||||
movimientos?: any[];
|
||||
movimientos?: MovimientoIndicacion[];
|
||||
canEdit: boolean;
|
||||
onAgregarEvolucion: (evolucion: Omit<Evolucion, 'id'>) => void;
|
||||
onActualizarEvolucion: (id: string, datos: Partial<Evolucion>) => void;
|
||||
@@ -104,33 +98,11 @@ interface HistoriaClinicaProps {
|
||||
onAgregarIndicacion: (indicacion: Omit<Indicacion, 'id'>) => void;
|
||||
onActualizarIndicacion: (id: string, datos: Partial<Indicacion>) => void;
|
||||
onEliminarIndicacion: (id: string) => void;
|
||||
onAgregarMovimiento?: (movimiento: any) => void;
|
||||
onActualizarInternacion: (id: string, datos: Partial<Internacion>) => void;
|
||||
onActualizarCama: (id: string, datos: Partial<Cama>) => void;
|
||||
onAgregarMovimiento?: (movimiento: MovimientoIndicacion) => void;
|
||||
onVolver: () => 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 }> = {
|
||||
'Hematocrito': { min: 36, max: 42 },
|
||||
'Hemoglobina': { min: 12, max: 14 },
|
||||
@@ -201,7 +173,7 @@ function calcularEstadoLaboratorio(parametro: string, valor: string): '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 lines: string[] = [];
|
||||
let currentLine = '';
|
||||
@@ -226,7 +198,6 @@ export function HistoriaClinica({
|
||||
internacion,
|
||||
paciente,
|
||||
cama,
|
||||
allCamas,
|
||||
evoluciones,
|
||||
laboratorios,
|
||||
glucemias,
|
||||
@@ -265,8 +236,6 @@ export function HistoriaClinica({
|
||||
onEliminarIndicacion,
|
||||
movimientos,
|
||||
onAgregarMovimiento,
|
||||
onActualizarInternacion,
|
||||
onActualizarCama,
|
||||
onVolver,
|
||||
onEditarIngreso,
|
||||
canEdit,
|
||||
@@ -590,11 +559,11 @@ export function HistoriaClinica({
|
||||
</ScrollArea>
|
||||
|
||||
<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 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 value="evoluciones" className="mt-4">
|
||||
@@ -602,11 +571,11 @@ export function HistoriaClinica({
|
||||
</TabsContent>
|
||||
|
||||
<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 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 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[];
|
||||
patientId: string;
|
||||
internacionId: string;
|
||||
add: (g: Omit<Glucemia, 'id'>) => void;
|
||||
update: (id: string, data: Partial<Glucemia>) => void;
|
||||
del: (id: string) => void;
|
||||
@@ -725,6 +695,7 @@ function SeccionGlucemias({ glucemias, patientId, add, update, del, canEdit }: {
|
||||
} else {
|
||||
add({
|
||||
pacienteId: patientId,
|
||||
internacionId,
|
||||
fecha,
|
||||
hora,
|
||||
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[];
|
||||
patientId: string;
|
||||
internacionId: string;
|
||||
add: (l: Omit<Laboratorio, 'id'>) => void;
|
||||
update: (id: string, data: Partial<Laboratorio>) => void;
|
||||
del: (id: string) => void;
|
||||
@@ -938,13 +910,11 @@ function SeccionLaboratorios({ lab, patientId, add, update, del, addAcidoBase, c
|
||||
const [dialog, setDialog] = useState(false);
|
||||
const [obsDialog, setObsDialog] = useState(false);
|
||||
const [evolDialog, setEvolDialog] = useState(false);
|
||||
const [selectedLab, setSelectedLab] = useState<Laboratorio | null>(null);
|
||||
const [edit, setEdit] = useState<Laboratorio | null>(null);
|
||||
const [obsLab, setObsLab] = useState<Laboratorio | null>(null);
|
||||
const [fecha, setFecha] = useState(new Date().toISOString().split('T')[0]);
|
||||
const [hora, setHora] = useState(new Date().toTimeString().slice(0, 5));
|
||||
const [observaciones, setObservaciones] = useState('');
|
||||
const [resultados, setResultados] = useState<ResultadoLaboratorio[]>([]);
|
||||
const [hto, setHto] = useState('');
|
||||
const [hb, setHb] = 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 };
|
||||
});
|
||||
|
||||
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 r = l.resultados.find(r => r.parametro === param);
|
||||
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 (importAcidoBase) {
|
||||
addAcidoBase({ ...importAcidoBase, fecha: importFecha, hora: importHora });
|
||||
addAcidoBase({ ...importAcidoBase, pacienteId: patientId, internacionId, fecha: importFecha, hora: importHora });
|
||||
}
|
||||
|
||||
add({
|
||||
@@ -1407,7 +1370,7 @@ function SeccionLaboratorios({ lab, patientId, add, update, del, addAcidoBase, c
|
||||
if (edit) {
|
||||
update(edit.id, { fecha, hora, resultados: resultadosLaboratorio, observaciones });
|
||||
} else {
|
||||
add({ pacienteId: patientId || '', fecha, hora, resultados: resultadosLaboratorio, observaciones });
|
||||
add({ pacienteId: patientId || '', internacionId, fecha, hora, resultados: resultadosLaboratorio, observaciones });
|
||||
}
|
||||
setDialog(false);
|
||||
reset();
|
||||
@@ -1761,7 +1724,6 @@ function SeccionEvoluciones({ evos, internacionId, add, update, del, canEdit }:
|
||||
const [dialog, setDialog] = useState(false);
|
||||
const [edit, setEdit] = 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 [hora, setHora] = useState(new Date().toTimeString().slice(0, 5));
|
||||
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[];
|
||||
patientId: string;
|
||||
internacionId: string;
|
||||
add: (a: Omit<AcidoBase, 'id'>) => void;
|
||||
update: (id: string, datos: Partial<AcidoBase>) => 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 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 phVal = parseFloat(ph), pco2Val = parseFloat(pco2), hco3Val = parseFloat(hco3), beVal = parseFloat(be);
|
||||
if (!phVal || !pco2Val || !hco3Val) return '';
|
||||
@@ -2136,16 +2090,10 @@ function SeccionAcidosBase({ ab, patientId, add, update, del, canEdit }: {
|
||||
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 = () => {
|
||||
if (!ph || !pco2 || !po2 || !hco3 || !be || !sato2) return;
|
||||
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);
|
||||
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[];
|
||||
patient: Paciente;
|
||||
internacionId: string;
|
||||
add: (c: Omit<Cultivo, 'id'>) => void;
|
||||
update: (id: string, data: Partial<Cultivo>) => void;
|
||||
del: (id: string) => void;
|
||||
@@ -2312,7 +2261,6 @@ function SeccionCultivos({ cults, patient, add, update, del, canEdit }: {
|
||||
const [resDialog, setResDialog] = useState(false);
|
||||
const [editDialog, setEditDialog] = useState(false);
|
||||
const [selected, setSelected] = useState<Cultivo | null>(null);
|
||||
const [isParcialMode, setIsParcialMode] = useState(false);
|
||||
const [fechaToma, setFechaToma] = useState(new Date().toISOString().split('T')[0]);
|
||||
const [protocolo, setProtocolo] = useState('');
|
||||
const [tipoMuestra, setTipoMuestra] = useState<Cultivo['tipoMuestra']>('HMCx2');
|
||||
@@ -2340,29 +2288,8 @@ function SeccionCultivos({ cults, patient, add, update, del, canEdit }: {
|
||||
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) => {
|
||||
setSelected(c);
|
||||
setIsParcialMode(true);
|
||||
setProtocolo(c.protocolo || '');
|
||||
setGermen(c.germen || '');
|
||||
setSensible(c.sensible || '');
|
||||
@@ -2372,7 +2299,6 @@ function SeccionCultivos({ cults, patient, add, update, del, canEdit }: {
|
||||
|
||||
const openDefinitivo = (c: Cultivo) => {
|
||||
setSelected(c);
|
||||
setIsParcialMode(false);
|
||||
setProtocolo(c.protocolo || '');
|
||||
setFechaResultado(c.fechaResultado || new Date().toISOString().split('T')[0]);
|
||||
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 = () => {
|
||||
if (!selected) return;
|
||||
@@ -2753,7 +2679,7 @@ function SeccionInterconsultas({ interconsultas, pacienteId, internacionId, add,
|
||||
|
||||
const handleGuardar = () => {
|
||||
if (!servicioInterconsultado) return;
|
||||
const datos: any = { fecha, servicioInterconsultado, motivo, respuestaInterconsulta };
|
||||
const datos: Partial<Interconsulta> = { fecha, servicioInterconsultado, motivo, respuestaInterconsulta };
|
||||
if (respuestaInterconsulta) {
|
||||
datos.respuestaFecha = respuestaFecha || new Date().toISOString().split('T')[0];
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
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 { Badge } from '@/components/ui/badge';
|
||||
import { Button } from '@/components/ui/button';
|
||||
@@ -28,6 +28,7 @@ interface InternacionesProps {
|
||||
diagnosticoEgreso: string;
|
||||
motivoEgreso: Internacion['motivoEgreso']
|
||||
}) => void;
|
||||
onEliminarInternacion?: (internacionId: string) => Promise<void> | void;
|
||||
getPacienteById: (id: string) => Paciente | undefined;
|
||||
getCamaById: (id: string) => Cama | undefined;
|
||||
onVerHC?: (internacionId: string) => void;
|
||||
@@ -40,6 +41,7 @@ export function Internaciones({
|
||||
camas,
|
||||
onIniciarInternacion,
|
||||
onFinalizarInternacion,
|
||||
onEliminarInternacion,
|
||||
getPacienteById,
|
||||
getCamaById,
|
||||
grupos,
|
||||
@@ -50,7 +52,9 @@ export function Internaciones({
|
||||
const [filtroEstado, setFiltroEstado] = useState<'todas' | 'activas' | 'finalizadas'>('todas');
|
||||
const [dialogoNuevaAbierto, setDialogoNuevaAbierto] = useState(false);
|
||||
const [dialogoEgresoAbierto, setDialogoEgresoAbierto] = useState(false);
|
||||
const [dialogoEliminarAbierto, setDialogoEliminarAbierto] = useState(false);
|
||||
const [internacionSeleccionada, setInternacionSeleccionada] = useState<Internacion | null>(null);
|
||||
const [internacionAEliminar, setInternacionAEliminar] = useState<Internacion | null>(null);
|
||||
const [busqueda, setBusqueda] = useState('');
|
||||
const [pacienteSeleccionado, setPacienteSeleccionado] = useState<string>('');
|
||||
const [camaSeleccionada, setCamaSeleccionada] = useState<string>('');
|
||||
@@ -488,6 +492,16 @@ export function Internaciones({
|
||||
<FileText className="mr-2 h-4 w-4" />
|
||||
Ver HC
|
||||
</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 && (
|
||||
<DropdownMenuItem onClick={() => abrirDialogoEgreso(internacion)}>
|
||||
<LogOut className="mr-2 h-4 w-4" />
|
||||
@@ -567,7 +581,36 @@ export function Internaciones({
|
||||
</div>
|
||||
</DialogContent>
|
||||
</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>
|
||||
);
|
||||
}
|
||||
|
||||
+18
-7
@@ -1,3 +1,4 @@
|
||||
import { useState } from 'react';
|
||||
import {
|
||||
LayoutDashboard,
|
||||
Bed,
|
||||
@@ -35,6 +36,7 @@ interface LayoutProps {
|
||||
|
||||
export function Layout({ children, vistaActual, onCambiarVista, currentUser, onLogout }: LayoutProps) {
|
||||
const { theme, setTheme } = useTheme()
|
||||
const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
|
||||
|
||||
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 });
|
||||
}
|
||||
|
||||
const renderNavContent = () => (
|
||||
const renderNavContent = (onItemClick?: () => void) => (
|
||||
<nav className="flex flex-col gap-2">
|
||||
{filteredMenuItems.map((item) => {
|
||||
const Icon = item.icon;
|
||||
@@ -69,7 +71,10 @@ export function Layout({ children, vistaActual, onCambiarVista, currentUser, onL
|
||||
key={item.vista}
|
||||
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'}`}
|
||||
onClick={() => onCambiarVista(item.vista)}
|
||||
onClick={() => {
|
||||
onCambiarVista(item.vista);
|
||||
if (onItemClick) onItemClick();
|
||||
}}
|
||||
>
|
||||
<Icon className="h-5 w-5" />
|
||||
<span>{item.label}</span>
|
||||
@@ -141,9 +146,9 @@ export function Layout({ children, vistaActual, onCambiarVista, currentUser, onL
|
||||
</div>
|
||||
<span className="font-bold text-gray-900 dark:text-white">Gestion Historia Clinica Electronica</span>
|
||||
</div>
|
||||
<Sheet>
|
||||
<Sheet open={mobileMenuOpen} onOpenChange={setMobileMenuOpen}>
|
||||
<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" />
|
||||
</Button>
|
||||
</SheetTrigger>
|
||||
@@ -162,13 +167,16 @@ export function Layout({ children, vistaActual, onCambiarVista, currentUser, onL
|
||||
</div>
|
||||
</div>
|
||||
<div className="p-4">
|
||||
{renderNavContent()}
|
||||
{renderNavContent(() => setMobileMenuOpen(false))}
|
||||
</div>
|
||||
<div className="p-4 border-t border-gray-200 dark:border-gray-700 space-y-2">
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={toggleDarkMode}
|
||||
onClick={() => {
|
||||
toggleDarkMode();
|
||||
setMobileMenuOpen(false);
|
||||
}}
|
||||
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" />}
|
||||
@@ -177,7 +185,10 @@ export function Layout({ children, vistaActual, onCambiarVista, currentUser, onL
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={onLogout}
|
||||
onClick={() => {
|
||||
onLogout();
|
||||
setMobileMenuOpen(false);
|
||||
}}
|
||||
className="w-full flex items-center justify-center gap-2 dark:text-white"
|
||||
>
|
||||
<LogOut className="h-4 w-4" />
|
||||
|
||||
@@ -21,8 +21,8 @@ export function Login() {
|
||||
try {
|
||||
await login(dni, password);
|
||||
window.location.reload();
|
||||
} catch (err: any) {
|
||||
setError(err.message || 'Error de autenticación');
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : 'Error de autenticación');
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
|
||||
@@ -18,10 +18,10 @@ interface MapaCamasProps {
|
||||
pacientes: Paciente[];
|
||||
internaciones: Internacion[];
|
||||
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;
|
||||
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;
|
||||
onEliminarGrupo: (id: string) => void;
|
||||
getPacienteById: (id: string) => Paciente | undefined;
|
||||
@@ -41,9 +41,8 @@ export function MapaCamas({
|
||||
onActualizarGrupo,
|
||||
onEliminarGrupo,
|
||||
getPacienteById,
|
||||
getInternacionById
|
||||
}: MapaCamasProps) {
|
||||
const { canEditCama, canAccessGrupo, currentUser } = useHospitalStore();
|
||||
const { canEditCama, currentUser } = useHospitalStore();
|
||||
const [filtroSala, setFiltroSala] = useState<string>('todas');
|
||||
const [filtroTipo, setFiltroTipo] = useState<string>('todos');
|
||||
const [filtroEstado, setFiltroEstado] = useState<string>('todos');
|
||||
@@ -479,9 +478,7 @@ export function MapaCamas({
|
||||
)}
|
||||
{canEditCama(cama.id) && (
|
||||
<Button variant="destructive" onClick={() => {
|
||||
if (confirm('Eliminar esta cama?')) {
|
||||
onEliminarCama(cama.id);
|
||||
}
|
||||
onEliminarCama(cama.id);
|
||||
}}>
|
||||
<Trash className="h-4 w-4 mr-1" />Eliminar
|
||||
</Button>
|
||||
@@ -589,7 +586,7 @@ export function MapaCamas({
|
||||
</div>
|
||||
<div className="flex gap-2 justify-end">
|
||||
{editingBed && (
|
||||
<Button variant="destructive" onClick={() => { if (confirm('Eliminar cama?')) { onEliminarCama(editingBed.id); setBedDialogOpen(false); } }}>
|
||||
<Button variant="destructive" onClick={() => { onEliminarCama(editingBed.id); setBedDialogOpen(false); }}>
|
||||
Eliminar
|
||||
</Button>
|
||||
)}
|
||||
|
||||
@@ -21,9 +21,10 @@ interface NuevoIngresoProps {
|
||||
onActualizarPaciente?: (id: string, datos: Partial<Paciente>) => void;
|
||||
onVolver: () => void;
|
||||
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 [pacienteSeleccionado, setPacienteSeleccionado] = useState<string>('');
|
||||
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 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 match = numero.match(/^(\d{3})-(\d+)$/);
|
||||
|
||||
@@ -225,6 +225,17 @@ export interface Indicacion {
|
||||
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 RolUsuario = 'admin' | 'medico' | 'enfermero';
|
||||
|
||||
Reference in New Issue
Block a user