From 3cb381acd37fc47791cda7a72f86dcf2cb2cffd5 Mon Sep 17 00:00:00 2001 From: Sergio Nicolas Lavaise Date: Sun, 9 Aug 2026 17:37:19 +0000 Subject: [PATCH] Actualizacion de tabla de internaciones y evoluciones --- src/App.tsx | 2 + src/sections/Evoluciones.tsx | 299 ++++++++++++++--------- src/sections/HistoriaClinica.tsx | 194 ++++++++++++--- src/sections/Internaciones.tsx | 353 ++++++++++++++++----------- src/sections/SeccionIndicaciones.tsx | 48 ++-- 5 files changed, 573 insertions(+), 323 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index ad47b93..41bd971 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -96,6 +96,8 @@ function AppContent() { evoluciones={store.evoluciones} internaciones={store.internaciones} pacientes={store.pacientes} + camas={store.camas} + getCamaById={store.getCamaById} onAgregarEvolucion={store.agregarEvolucion} onActualizarEvolucion={store.actualizarEvolucion} onEliminarEvolucion={store.eliminarEvolucion} diff --git a/src/sections/Evoluciones.tsx b/src/sections/Evoluciones.tsx index b62213c..6bec4e3 100644 --- a/src/sections/Evoluciones.tsx +++ b/src/sections/Evoluciones.tsx @@ -1,19 +1,22 @@ import { useState } from 'react'; -import { FileText, Plus, Search, Calendar, Clock, Thermometer, Heart, Activity, Wind, Droplets, Pencil, Trash2, ChevronDown, ChevronUp, X, Save } from 'lucide-react'; +import { FileText, Plus, Search, Eye, MoreHorizontal, Pencil, Trash2, X, Save, Activity, Heart, Wind, Thermometer, Droplets } from 'lucide-react'; import { Card, CardContent } from '@/components/ui/card'; -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 { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'; +import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table'; +import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from '@/components/ui/dropdown-menu'; import { toast } from 'sonner'; -import type { Evolucion, Internacion, Paciente, SignosVitales, ExamenFisico } from '@/types'; +import type { Evolucion, Internacion, Paciente, Cama, SignosVitales, ExamenFisico } from '@/types'; interface EvolucionesProps { evoluciones: Evolucion[]; internaciones: Internacion[]; pacientes: Paciente[]; + camas?: Cama[]; + getCamaById?: (id: string) => Cama | undefined; onAgregarEvolucion: (evolucion: Omit) => Promise | void; onActualizarEvolucion?: (id: string, datos: Partial) => Promise | void; onEliminarEvolucion: (id: string) => Promise | void; @@ -25,6 +28,8 @@ export function Evoluciones({ evoluciones, internaciones, pacientes, + camas, + getCamaById, onAgregarEvolucion, onActualizarEvolucion, onEliminarEvolucion, @@ -34,7 +39,7 @@ export function Evoluciones({ const [busqueda, setBusqueda] = useState(''); const [pacienteSeleccionado, setPacienteSeleccionado] = useState(''); const [dialogoAbierto, setDialogoAbierto] = useState(false); - const [evolucionExpandida, setEvolucionExpandida] = useState(null); + const [evolucionDetalle, setEvolucionDetalle] = useState(null); const [evolucionEditando, setEvolucionEditando] = useState(null); const [fecha, setFecha] = useState(new Date().toISOString().split('T')[0]); @@ -439,120 +444,83 @@ export function Evoluciones({ -
- {evolucionesFiltradas.map((evolucion) => { - const internacion = internaciones.find(i => i.id === evolucion.internacionId); - const paciente = internacion ? getPacienteById(internacion.pacienteId) : null; - const signosTexto = getSignosVitalesTexto(evolucion.signosVitales); - const estaExpandida = evolucionExpandida === evolucion.id; + {evolucionesFiltradas.length > 0 ? ( +
+ + + + Fecha + Cama + Paciente + Profesional + TAS + TAD + FC + FR + + Sat + Acciones + + + + {evolucionesFiltradas.map((evolucion) => { + const internacion = internaciones.find(i => i.id === evolucion.internacionId); + const paciente = internacion ? getPacienteById(internacion.pacienteId) : null; + const cama = internacion ? (getCamaById ? getCamaById(internacion.camaId) : camas?.find(c => c.id === internacion.camaId)) : null; + const sv = evolucion.signosVitales; -return ( - - -
- {/* Patient Info */} -
-
-
- -
-
-

- {paciente ? `${paciente.apellido}, ${paciente.nombre}` : 'Paciente no encontrado'} -

-
- - - {evolucion.fecha} - - - - {evolucion.hora} - - {evolucion.medico} -
-
-
-
- - {onActualizarEvolucion && ( - - )} - -
-
- - {signosTexto && ( -
-

- - Signos Vitales: {signosTexto} -

-
- )} - - {estaExpandida && ( - <> - {evolucion.examenFisico && ( -
-

Examen Físico:

-
- {evolucion.examenFisico.SNC &&
SNC: {evolucion.examenFisico.SNC}
} - {evolucion.examenFisico.Cardiovascular &&
CV: {evolucion.examenFisico.Cardiovascular}
} - {evolucion.examenFisico.Respiratorio &&
Resp: {evolucion.examenFisico.Respiratorio}
} - {evolucion.examenFisico.Abdominal &&
Abd: {evolucion.examenFisico.Abdominal}
} - {evolucion.examenFisico.Genitourinario &&
GU: {evolucion.examenFisico.Genitourinario}
} - {evolucion.examenFisico.PielAnexos &&
Piel: {evolucion.examenFisico.PielAnexos}
} - {evolucion.examenFisico.SOMA &&
SOMA: {evolucion.examenFisico.SOMA}
} -
-
- )} - - {evolucion.novedades && ( -
-

Novedades:

-

{evolucion.novedades}

-
- )} - - {evolucion.comentario && ( -
-

Comentario:

-

{evolucion.comentario}

-
- )} - - {evolucion.pendientes && ( -
-

Pendientes:

-

{evolucion.pendientes}

-
- )} - - )} -
-
-
- ); - })} - - - {evolucionesFiltradas.length === 0 && ( + return ( + + + {evolucion.fecha} {evolucion.hora || ''} + + + {cama ? cama.numero : '-'} + + + {paciente ? `${paciente.apellido}, ${paciente.nombre}` : 'Sin paciente'} + + + {evolucion.medico || '-'} + + {sv?.presionSistolica ?? '-'} + {sv?.presionDiastolica ?? '-'} + {sv?.frecuenciaCardiaca ?? '-'} + {sv?.frecuenciaRespiratoria ?? '-'} + {sv?.temperatura !== undefined ? `${sv.temperatura}°C` : '-'} + {sv?.saturacionO2 !== undefined ? `${sv.saturacionO2}%` : '-'} + + + + + + + setEvolucionDetalle(evolucion)}> + + Detalles + + {onActualizarEvolucion && ( + abrirEditar(evolucion)}> + + Editar + + )} + onEliminarEvolucion(evolucion.id)} className="text-red-600 focus:text-red-600"> + + Eliminar + + + + + + ); + })} +
+
+
+ ) : (

@@ -560,6 +528,103 @@ return (

)} + + {/* Modal de Detalle de Evolución */} + { if (!open) setEvolucionDetalle(null); }}> + + + + + Detalle de Evolución + + + {evolucionDetalle && (() => { + const internacionDet = internaciones.find(i => i.id === evolucionDetalle.internacionId); + const pacienteDet = internacionDet ? getPacienteById(internacionDet.pacienteId) : null; + const camaDet = internacionDet ? (getCamaById ? getCamaById(internacionDet.camaId) : camas?.find(c => c.id === internacionDet.camaId)) : null; + const svDet = evolucionDetalle.signosVitales; + const efDet = evolucionDetalle.examenFisico; + + return ( +
+
+
+ {pacienteDet && ( +

+ {pacienteDet.apellido}, {pacienteDet.nombre} (DNI: {pacienteDet.dni}{camaDet ? ` | Cama: ${camaDet.numero}` : ''}) +

+ )} +

+ Fecha: {evolucionDetalle.fecha}  |  Hora: {evolucionDetalle.hora} +

+
+
+

Médico: {evolucionDetalle.medico}

+
+
+ + {/* Signos Vitales */} + {svDet && ( +
+

+ + Signos Vitales +

+
+
TAS: {svDet.presionSistolica ?? '-'} mmHg
+
TAD: {svDet.presionDiastolica ?? '-'} mmHg
+
FC: {svDet.frecuenciaCardiaca ?? '-'} lpm
+
FR: {svDet.frecuenciaRespiratoria ?? '-'} rpm
+
Tº: {svDet.temperatura !== undefined ? `${svDet.temperatura}°C` : '-'}
+
SatO2: {svDet.saturacionO2 !== undefined ? `${svDet.saturacionO2}%` : '-'}
+
+
+ )} + + {/* Examen Físico */} + {efDet && ( +
+

Examen Físico:

+
+ {efDet.SNC &&
SNC: {efDet.SNC}
} + {efDet.Cardiovascular &&
Cardiovascular: {efDet.Cardiovascular}
} + {efDet.Respiratorio &&
Respiratorio: {efDet.Respiratorio}
} + {efDet.Abdominal &&
Abdominal: {efDet.Abdominal}
} + {efDet.Genitourinario &&
Genitourinario: {efDet.Genitourinario}
} + {efDet.PielAnexos &&
Piel y Anexos: {efDet.PielAnexos}
} + {efDet.SOMA &&
SOMA: {efDet.SOMA}
} +
+
+ )} + + {/* Novedades */} + {evolucionDetalle.novedades && ( +
+

Novedades:

+

{evolucionDetalle.novedades}

+
+ )} + + {/* Comentario */} + {evolucionDetalle.comentario && ( +
+

Comentario:

+

{evolucionDetalle.comentario}

+
+ )} + + {/* Pendientes */} + {evolucionDetalle.pendientes && ( +
+

Pendientes:

+

{evolucionDetalle.pendientes}

+
+ )} +
+ ); + })()} +
+
); } diff --git a/src/sections/HistoriaClinica.tsx b/src/sections/HistoriaClinica.tsx index 7c4bb5d..2839665 100644 --- a/src/sections/HistoriaClinica.tsx +++ b/src/sections/HistoriaClinica.tsx @@ -33,6 +33,7 @@ import { Save, X, MoreHorizontal, + Eye, Edit, ClipboardList, TrendingUp, @@ -269,6 +270,7 @@ export function HistoriaClinica({ canEdit, }: HistoriaClinicaProps) { const [tabActivo, setTabActivo] = useState('evoluciones'); + const indicacionesActivas = (indicadores || []).filter(i => i.estado === 'Activa'); const calcularEdad = (fechaNacimiento: string) => { @@ -544,7 +546,7 @@ export function HistoriaClinica({ Indicaciones - + ({indicacionesActivas.length}) @@ -1755,6 +1757,7 @@ function SeccionEvoluciones({ evos, internacionId, add, update, del, canEdit }: }) { const [dialog, setDialog] = useState(false); const [edit, setEdit] = useState(null); + const [evoDetalle, setEvoDetalle] = useState(null); const [expanded, setExpanded] = useState(null); const [fecha, setFecha] = useState(new Date().toISOString().split('T')[0]); const [hora, setHora] = useState(new Date().toTimeString().slice(0, 5)); @@ -1900,42 +1903,165 @@ function SeccionEvoluciones({ evos, internacionId, add, update, del, canEdit }:
- {evosFiltered.length === 0 ?

No hay evoluciones

: - evosFiltered.map(e => { - const isExpanded = expanded === e.id; - const sv = e.signosVitales; - const svText = sv ? [sv.presionSistolica && sv.presionDiastolica ? `PA: ${sv.presionSistolica}/${sv.presionDiastolica}` : null, sv.frecuenciaCardiaca ? `FC: ${sv.frecuenciaCardiaca}` : null, sv.frecuenciaRespiratoria ? `FR: ${sv.frecuenciaRespiratoria}` : null, sv.temperatura ? `T: ${sv.temperatura}°C` : null, sv.saturacionO2 ? `SatO2: ${sv.saturacionO2}%` : null].filter(Boolean).join(' | ') : null; - return ( - -
-
-

{e.fecha} {e.hora}

Médico: {e.medico}

-
- - {canEdit && } - {canEdit && } + {evosFiltered.length === 0 ? ( +
+ +

No hay evoluciones

+
+ ) : ( +
+ + + + Fecha + Profesional + TAS + TAD + FC + FR + + Sat + Acciones + + + + {evosFiltered.map((e) => { + const sv = e.signosVitales; + return ( + + + {e.fecha} {e.hora || ''} + + + {e.medico || '-'} + + {sv?.presionSistolica ?? '-'} + {sv?.presionDiastolica ?? '-'} + {sv?.frecuenciaCardiaca ?? '-'} + {sv?.frecuenciaRespiratoria ?? '-'} + {sv?.temperatura !== undefined ? `${sv.temperatura}°C` : '-'} + {sv?.saturacionO2 !== undefined ? `${sv.saturacionO2}%` : '-'} + + + + + + + setEvoDetalle(e)}> + + Detalles + + {canEdit && ( + openEdit(e)}> + + Editar + + )} + {canEdit && ( + del(e.id)} className="text-red-600 focus:text-red-600"> + + Eliminar + + )} + + + + + ); + })} + +
+
+ )} + + {/* Modal Detalles Evolucion */} + { if (!open) setEvoDetalle(null); }}> + + + + + Detalle de Evolución + + + {evoDetalle && (() => { + const svDet = evoDetalle.signosVitales; + const efDet = evoDetalle.examenFisico; + + return ( +
+
+
+

Fecha: {evoDetalle.fecha}  |  Hora: {evoDetalle.hora}

+
+
+

Médico: {evoDetalle.medico}

- {svText &&

Signos Vitales: {svText}

} - {isExpanded && <> - {e.examenFisico &&

Examen Físico:

- {e.examenFisico.SNC &&
SNC: {e.examenFisico.SNC}
} - {e.examenFisico.Cardiovascular &&
CV: {e.examenFisico.Cardiovascular}
} - {e.examenFisico.Respiratorio &&
Resp: {e.examenFisico.Respiratorio}
} - {e.examenFisico.Abdominal &&
Abd: {e.examenFisico.Abdominal}
} - {e.examenFisico.Genitourinario &&
GU: {e.examenFisico.Genitourinario}
} - {e.examenFisico.PielAnexos &&
Piel: {e.examenFisico.PielAnexos}
} - {e.examenFisico.SOMA &&
SOMA: {e.examenFisico.SOMA}
} -
} - {e.novedades &&

Novedades:

{e.novedades}

} - {e.comentario &&

Comentario:

{e.comentario}

} - {e.pendientes &&

Pendientes:

{e.pendientes}

} - } + + {/* Signos Vitales */} + {svDet && ( +
+

+ + Signos Vitales +

+
+
TAS: {svDet.presionSistolica ?? '-'} mmHg
+
TAD: {svDet.presionDiastolica ?? '-'} mmHg
+
FC: {svDet.frecuenciaCardiaca ?? '-'} lpm
+
FR: {svDet.frecuenciaRespiratoria ?? '-'} rpm
+
Tº: {svDet.temperatura !== undefined ? `${svDet.temperatura}°C` : '-'}
+
SatO2: {svDet.saturacionO2 !== undefined ? `${svDet.saturacionO2}%` : '-'}
+
+
+ )} + + {/* Examen Físico */} + {efDet && ( +
+

Examen Físico:

+
+ {efDet.SNC &&
SNC: {efDet.SNC}
} + {efDet.Cardiovascular &&
Cardiovascular: {efDet.Cardiovascular}
} + {efDet.Respiratorio &&
Respiratorio: {efDet.Respiratorio}
} + {efDet.Abdominal &&
Abdominal: {efDet.Abdominal}
} + {efDet.Genitourinario &&
Genitourinario: {efDet.Genitourinario}
} + {efDet.PielAnexos &&
Piel y Anexos: {efDet.PielAnexos}
} + {efDet.SOMA &&
SOMA: {efDet.SOMA}
} +
+
+ )} + + {/* Novedades */} + {evoDetalle.novedades && ( +
+

Novedades:

+

{evoDetalle.novedades}

+
+ )} + + {/* Comentario */} + {evoDetalle.comentario && ( +
+

Comentario:

+

{evoDetalle.comentario}

+
+ )} + + {/* Pendientes */} + {evoDetalle.pendientes && ( +
+

Pendientes:

+

{evoDetalle.pendientes}

+
+ )}
- - ); - }) - } + ); + })()} +
+
); } diff --git a/src/sections/Internaciones.tsx b/src/sections/Internaciones.tsx index 66bf86b..0d4cb69 100644 --- a/src/sections/Internaciones.tsx +++ b/src/sections/Internaciones.tsx @@ -1,5 +1,5 @@ import { useState } from 'react'; -import { ClipboardList, Plus, Search, Bed, Calendar, Stethoscope, LogOut, CheckCircle2 } from 'lucide-react'; +import { ClipboardList, Plus, Search, LogOut, CheckCircle2, MoreHorizontal, FileText } from 'lucide-react'; import { Card, CardContent } from '@/components/ui/card'; import { Badge } from '@/components/ui/badge'; import { Button } from '@/components/ui/button'; @@ -7,6 +7,8 @@ import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from '@/components/ui/dialog'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'; +import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table'; +import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from '@/components/ui/dropdown-menu'; import { toast } from 'sonner'; import type { Internacion, Paciente, Cama, Area, Evolucion, Laboratorio, Cultivo } from '@/types'; import { formatDateDDMMYYYY } from '@/lib/utils'; @@ -165,6 +167,44 @@ export function Internaciones({ const getAreaName = (areaId?: string) => areas.find(a => a.id === areaId)?.nombre; + const calcularEdad = (fechaNacimiento?: string) => { + if (!fechaNacimiento) return '-'; + const hoy = new Date(); + const nacimiento = new Date(fechaNacimiento); + if (isNaN(nacimiento.getTime())) return '-'; + let edad = hoy.getFullYear() - nacimiento.getFullYear(); + const m = hoy.getMonth() - nacimiento.getMonth(); + if (m < 0 || (m === 0 && hoy.getDate() < nacimiento.getDate())) { + edad--; + } + return edad >= 0 ? `${edad} años` : '-'; + }; + + const calcularDiasDuracion = (internacion: Internacion) => { + const fechaStr = internacion.fechaIngresoClinica || internacion.fechaIngreso; + if (!fechaStr) return null; + + const hoy = new Date(); + let fechaFin = hoy; + + if (!internacion.activa && internacion.fechaEgreso) { + const egreso = new Date(internacion.fechaEgreso.includes('T') ? internacion.fechaEgreso : `${internacion.fechaEgreso}T00:00:00`); + if (!isNaN(egreso.getTime())) { + fechaFin = egreso; + } + } + + const ingreso = new Date(fechaStr.includes('T') ? fechaStr : `${fechaStr}T00:00:00`); + if (isNaN(ingreso.getTime())) return null; + + const inicioD = new Date(ingreso.getFullYear(), ingreso.getMonth(), ingreso.getDate()); + const finD = new Date(fechaFin.getFullYear(), fechaFin.getMonth(), fechaFin.getDate()); + + const diffMs = finD.getTime() - inicioD.getTime(); + const dias = Math.floor(diffMs / (1000 * 60 * 60 * 24)); + return dias < 0 ? 0 : dias; + }; + const abrirDialogoEgreso = (internacion: Internacion) => { setInternacionSeleccionada(internacion); setFechaEgreso(new Date().toISOString().split('T')[0]); @@ -351,153 +391,8 @@ export function Internaciones({ - {/* Lista de Internaciones */} -
- {internacionesFiltradas.map((internacion) => { - const paciente = getPacienteById(internacion.pacienteId); - const cama = getCamaById(internacion.camaId); - - return ( - - -
-
-
- {internacion.activa ? ( - - ) : ( - - )} -
-
-
-

- {paciente ? `${paciente.apellido}, ${paciente.nombre}` : 'Paciente no encontrado'} -

- - {internacion.activa ? 'Activa' : 'Finalizada'} - -
-
-
- - Cama {cama?.numero || '-'} ({getAreaName(cama?.areaId) || 'Sin área'}) - -
-
- - Ingreso: {internacion.fechaIngresoClinica ? formatDateDDMMYYYY(internacion.fechaIngresoClinica) : 'N/A'} -
-
- - {internacion.medicoIngresante} -
-
-
-

- Diagnóstico de ingreso:{' '} - {internacion.diagnosticoIngreso} -

-
- {!internacion.activa && internacion.fechaEgreso && ( -
-
- - Egreso: {internacion.fechaEgreso} -
-

- Diagnóstico de egreso:{' '} - {internacion.diagnosticoEgreso} -

-

- Motivo:{' '} - {internacion.motivoEgreso} -

-
- )} -
-
-
- {internacion.activa && ( - - - - - - - Dar Alta - -
-
- - -
-
- - setFechaEgreso(e.target.value)} - /> -
-
- - -
-
- -