Fix: Organizacion de Editar Ingreso igual a Nuevo Ingreso, corregido formato de fecha
This commit is contained in:
+7
-4
@@ -47,7 +47,8 @@ db.exec(`
|
||||
id TEXT PRIMARY KEY,
|
||||
pacienteId TEXT NOT NULL,
|
||||
camaId TEXT,
|
||||
fechaIngreso TEXT NOT NULL,
|
||||
fechaIngresoHospital TEXT,
|
||||
fechaIngresoClinica TEXT,
|
||||
fechaEgreso TEXT,
|
||||
diagnosticoIngreso TEXT,
|
||||
motivoConsulta TEXT,
|
||||
@@ -56,7 +57,9 @@ db.exec(`
|
||||
diagnosticoEgreso TEXT,
|
||||
medicoIngresante TEXT,
|
||||
motivoEgreso TEXT,
|
||||
activa INTEGER DEFAULT 1
|
||||
activa INTEGER DEFAULT 1,
|
||||
apache TEXT,
|
||||
derivacion TEXT
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS evoluciones (
|
||||
@@ -200,9 +203,9 @@ app.put('/api/state', (req, res) => {
|
||||
}
|
||||
|
||||
if (state.internaciones?.length) {
|
||||
const stmt = db.prepare('INSERT INTO internaciones (id, pacienteId, camaId, fechaIngreso, fechaEgreso, diagnosticoIngreso, motivoConsulta, enfermedadActual, antecedentesEnfermedadActual, diagnosticoEgreso, medicoIngresante, motivoEgreso, activa) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)');
|
||||
const stmt = db.prepare('INSERT INTO internaciones (id, pacienteId, camaId, fechaIngresoHospital, fechaIngresoClinica, fechaEgreso, diagnosticoIngreso, motivoConsulta, enfermedadActual, antecedentesEnfermedadActual, diagnosticoEgreso, medicoIngresante, motivoEgreso, activa, apache, derivacion) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)');
|
||||
for (const i of state.internaciones) {
|
||||
stmt.run(i.id, i.pacienteId, i.camaId, i.fechaIngreso, i.fechaEgreso || null, i.diagnosticoIngreso || null, i.motivoConsulta || null, i.enfermedadActual || null, i.antecedentesEnfermedadActual || null, i.diagnosticoEgreso || null, i.medicoIngresante || null, i.motivoEgreso || null, i.activa ? 1 : 0);
|
||||
stmt.run(i.id, i.pacienteId, i.camaId, i.fechaIngresoHospital || null, i.fechaIngresoClinica || null, i.fechaEgreso || null, i.diagnosticoIngreso || null, i.motivoConsulta || null, i.enfermedadActual || null, i.antecedentesEnfermedadActual || null, i.diagnosticoEgreso || null, i.medicoIngresante || null, i.motivoEgreso || null, i.activa ? 1 : 0, i.apache || null, i.derivacion || null);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+40
-2
@@ -8,6 +8,8 @@ import { Evoluciones } from '@/sections/Evoluciones';
|
||||
import { AcidoBaseSection } from '@/sections/AcidoBase';
|
||||
import { Cultivos } from '@/sections/Cultivos';
|
||||
import { HistoriaClinica } from '@/sections/HistoriaClinica';
|
||||
import { NuevoIngreso } from '@/sections/NuevoIngreso';
|
||||
import { EditIngreso } from '@/sections/EditIngreso';
|
||||
import { Spinner } from '@/components/ui/spinner';
|
||||
import { Button } from '@/components/ui/button';
|
||||
|
||||
@@ -77,6 +79,7 @@ function App() {
|
||||
getPacienteById={store.getPacienteById}
|
||||
getCamaById={store.getCamaById}
|
||||
onVerHC={(id) => { store.setCurrentInternacion(id); store.setVista('historiaclinica'); }}
|
||||
onNuevoIngreso={() => store.setVista('nuevoingreso')}
|
||||
/>
|
||||
);
|
||||
case 'evoluciones':
|
||||
@@ -152,12 +155,47 @@ function App() {
|
||||
onActualizarInternacion={store.actualizarInternacion}
|
||||
onActualizarCama={store.actualizarCama}
|
||||
onVolver={() => store.setVista('internaciones')}
|
||||
onEditarIngreso={() => { store.setVista('editaringreso'); }}
|
||||
/>
|
||||
);
|
||||
}
|
||||
case 'nuevoingreso':
|
||||
return (
|
||||
<NuevoIngreso
|
||||
pacientes={store.pacientes}
|
||||
camas={store.camas}
|
||||
areas={store.areas}
|
||||
onIniciarInternacion={store.iniciarInternacion}
|
||||
onVolver={() => store.setVista('internaciones')}
|
||||
getAreaName={(areaId) => store.areas.find(a => a.id === areaId)?.nombre || ''}
|
||||
/>
|
||||
);
|
||||
|
||||
|
||||
default:
|
||||
case 'editaringreso': {
|
||||
const internacion = store.getInternacionById(store.currentInternacionId || '');
|
||||
const paciente = internacion ? store.getPacienteById(internacion.pacienteId) : undefined;
|
||||
if (!internacion || !paciente) {
|
||||
return (
|
||||
<div className="p-4 text-center">
|
||||
<p className="text-gray-500">Internación no encontrada</p>
|
||||
<Button onClick={() => store.setVista('internaciones')}>Volver a Internaciones</Button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<EditIngreso
|
||||
internacion={internacion}
|
||||
paciente={paciente}
|
||||
cama={store.getCamaById(internacion.camaId)}
|
||||
pacientes={store.pacientes}
|
||||
camas={store.camas}
|
||||
areas={store.areas}
|
||||
onActualizarInternacion={store.actualizarInternacion}
|
||||
onVolver={() => store.setVista('historiaclinica')}
|
||||
getAreaName={(areaId) => store.areas.find(a => a.id === areaId)?.nombre || ''}
|
||||
/>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -157,11 +157,10 @@ export function useHospitalStore() {
|
||||
}, []);
|
||||
|
||||
// Acciones de internaciones
|
||||
const iniciarInternacion = useCallback((internacion: Omit<Internacion, 'id' | 'fechaIngreso' | 'activa'>) => {
|
||||
const iniciarInternacion = useCallback((internacion: Omit<Internacion, 'id' | 'activa'>) => {
|
||||
const nuevaInternacion: Internacion = {
|
||||
...internacion,
|
||||
id: generateUUID(),
|
||||
fechaIngreso: new Date().toISOString().split('T')[0],
|
||||
activa: true,
|
||||
};
|
||||
setState(prev => ({
|
||||
|
||||
+3
-5
@@ -7,10 +7,8 @@ export function cn(...inputs: ClassValue[]) {
|
||||
|
||||
export function formatDateDDMMYYYY(dateString: string): string {
|
||||
if (!dateString) return '';
|
||||
const date = new Date(dateString);
|
||||
if (isNaN(date.getTime())) return dateString;
|
||||
const day = String(date.getDate()).padStart(2, '0');
|
||||
const month = String(date.getMonth() + 1).padStart(2, '0');
|
||||
const year = date.getFullYear();
|
||||
const parts = dateString.split('-');
|
||||
if (parts.length !== 3) return dateString;
|
||||
const [year, month, day] = parts;
|
||||
return `${day}-${month}-${year}`;
|
||||
}
|
||||
|
||||
@@ -210,7 +210,7 @@ export function Dashboard({
|
||||
{paciente ? `${paciente.apellido}, ${paciente.nombre}` : 'Paciente no encontrado'}
|
||||
</p>
|
||||
<p className="text-sm text-gray-500">
|
||||
Ingreso: {formatDateDDMMYYYY(internacion.fechaIngreso)}
|
||||
Ingreso: {internacion.fechaIngresoClinica ? formatDateDDMMYYYY(internacion.fechaIngresoClinica) : 'N/A'}
|
||||
</p>
|
||||
</div>
|
||||
<Badge variant="outline" className="bg-purple-50 text-purple-700">
|
||||
|
||||
@@ -0,0 +1,323 @@
|
||||
import { useState } from 'react';
|
||||
import { ArrowLeft, Save, X, Bed, Calendar, Stethoscope, User, ClipboardList } from 'lucide-react';
|
||||
import { Card, CardContent } from '@/components/ui/card';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Label } from '@/components/ui/label';
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import type { Paciente, Cama, Area, Internacion } from '@/types';
|
||||
|
||||
interface EditIngresoProps {
|
||||
internacion: Internacion;
|
||||
paciente: Paciente;
|
||||
cama: Cama | undefined;
|
||||
pacientes: Paciente[];
|
||||
camas: Cama[];
|
||||
areas: Area[];
|
||||
onActualizarInternacion: (id: string, datos: Partial<Internacion>) => void;
|
||||
onVolver: () => void;
|
||||
getAreaName: (areaId: string | undefined) => string;
|
||||
}
|
||||
|
||||
export function EditIngreso({
|
||||
internacion,
|
||||
paciente: pacienteOriginal,
|
||||
cama: camaOriginal,
|
||||
pacientes,
|
||||
camas,
|
||||
areas,
|
||||
onActualizarInternacion,
|
||||
onVolver,
|
||||
getAreaName
|
||||
}: EditIngresoProps) {
|
||||
const [pacienteSeleccionado, setPacienteSeleccionado] = useState(internacion.pacienteId);
|
||||
const [busqueda, setBusqueda] = useState('');
|
||||
const [camaSeleccionada, setCamaSeleccionada] = useState(internacion.camaId);
|
||||
const [medico, setMedico] = useState(internacion.medicoIngresante);
|
||||
const [fechaIngresoHospital, setFechaIngresoHospital] = useState(internacion.fechaIngresoHospital || '');
|
||||
const [fechaIngresoClinica, setFechaIngresoClinica] = useState(internacion.fechaIngresoClinica || '');
|
||||
const [motivoConsulta, setMotivoConsulta] = useState(internacion.motivoConsulta || '');
|
||||
const [diagnosticoIngreso, setDiagnosticoIngreso] = useState(internacion.diagnosticoIngreso);
|
||||
const [enfermedadActual, setEnfermedadActual] = useState(internacion.enfermedadActual);
|
||||
const [antecedentesEnfermedadActual, setAntecedentesEnfermedadActual] = useState(internacion.antecedentesEnfermedadActual || '');
|
||||
const [apache, setApache] = useState(internacion.apache || '');
|
||||
const [derivacion, setDerivacion] = useState(internacion.derivacion || '');
|
||||
|
||||
const selectedPaciente = pacientes.find(p => p.id === pacienteSeleccionado);
|
||||
|
||||
const handleSubmit = () => {
|
||||
if (!pacienteSeleccionado || !camaSeleccionada || !medico || !diagnosticoIngreso || !enfermedadActual) {
|
||||
alert('Por favor complete los campos obligatorios');
|
||||
return;
|
||||
}
|
||||
|
||||
onActualizarInternacion(internacion.id, {
|
||||
pacienteId: pacienteSeleccionado,
|
||||
camaId: camaSeleccionada,
|
||||
medicoIngresante: medico,
|
||||
diagnosticoIngreso,
|
||||
motivoConsulta: motivoConsulta || undefined,
|
||||
enfermedadActual,
|
||||
antecedentesEnfermedadActual: antecedentesEnfermedadActual || undefined,
|
||||
fechaIngresoHospital: fechaIngresoHospital || undefined,
|
||||
fechaIngresoClinica: fechaIngresoClinica || undefined,
|
||||
apache: apache || undefined,
|
||||
derivacion: derivacion || undefined,
|
||||
});
|
||||
|
||||
onVolver();
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-4">
|
||||
<Button variant="ghost" size="icon" onClick={onVolver}>
|
||||
<ArrowLeft className="h-5 w-5" />
|
||||
</Button>
|
||||
<div>
|
||||
<h1 className="text-2xl font-bold text-gray-900 flex items-center gap-2">
|
||||
<User className="h-6 w-6 text-blue-600" />
|
||||
Editar Ingreso
|
||||
</h1>
|
||||
<p className="text-gray-500">Modificar datos de internación</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex gap-2">
|
||||
<Button variant="outline" onClick={() => onVolver()}>
|
||||
<X className="h-4 w-4 mr-2" />
|
||||
Cancelar
|
||||
</Button>
|
||||
<Button className="bg-blue-600 hover:bg-blue-700" onClick={handleSubmit}>
|
||||
<Save className="h-4 w-4 mr-2" />
|
||||
Guardar Cambios
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
|
||||
{/* Columna 1: Datos del Paciente, Cama y Datos Adicionales */}
|
||||
<div className="space-y-6">
|
||||
<Card>
|
||||
<CardContent className="pt-6 space-y-4">
|
||||
<h3 className="font-semibold text-gray-900 flex items-center gap-2">
|
||||
<User className="h-4 w-4" />
|
||||
Datos del Paciente
|
||||
</h3>
|
||||
|
||||
{selectedPaciente ? (
|
||||
<div className="flex items-center gap-2 flex-wrap">
|
||||
<Badge className="bg-blue-100 text-blue-800">
|
||||
{selectedPaciente?.apellido}, {selectedPaciente?.nombre}
|
||||
</Badge>
|
||||
<Badge variant="outline">DNI: {selectedPaciente?.dni}</Badge>
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
<Label>Buscar Paciente *</Label>
|
||||
<Input
|
||||
placeholder="Apellido, nombre o DNI"
|
||||
value={busqueda}
|
||||
onChange={(e) => setBusqueda(e.target.value)}
|
||||
/>
|
||||
<div className="max-h-48 overflow-auto border rounded-md">
|
||||
{(() => {
|
||||
const q = busqueda.trim().toLowerCase();
|
||||
if (!q) {
|
||||
return <div className="p-2 text-sm text-gray-500">Escriba para buscar</div>;
|
||||
}
|
||||
const matches = pacientes.filter(p =>
|
||||
p.apellido.toLowerCase().includes(q) ||
|
||||
p.nombre.toLowerCase().includes(q) ||
|
||||
p.dni.includes(q)
|
||||
);
|
||||
if (matches.length === 0) {
|
||||
return <div className="p-2 text-sm text-gray-500">Sin resultados</div>;
|
||||
}
|
||||
return matches.map(p => (
|
||||
<button
|
||||
key={p.id}
|
||||
type="button"
|
||||
onClick={() => setPacienteSeleccionado(p.id)}
|
||||
className="w-full text-left p-2 hover:bg-gray-50 text-sm"
|
||||
>
|
||||
{p.apellido}, {p.nombre} — DNI: {p.dni}
|
||||
</button>
|
||||
));
|
||||
})()}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card>
|
||||
<CardContent className="pt-6 space-y-4">
|
||||
<h3 className="font-semibold text-gray-900 flex items-center gap-2">
|
||||
<Bed className="h-4 w-4" />
|
||||
Asignación de Cama
|
||||
</h3>
|
||||
|
||||
<div>
|
||||
<Label>Cama *</Label>
|
||||
<Select value={camaSeleccionada} onValueChange={setCamaSeleccionada}>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="Seleccionar cama" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{camas.map(c => (
|
||||
<SelectItem key={c.id} value={c.id}>
|
||||
{c.numero} - {getAreaName(c.areaId)} ({c.tipo})
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card>
|
||||
<CardContent className="pt-6 space-y-4">
|
||||
<h3 className="font-semibold text-gray-900 flex items-center gap-2">
|
||||
<ClipboardList className="h-4 w-4" />
|
||||
Datos Adicionales
|
||||
</h3>
|
||||
|
||||
<div>
|
||||
<Label>Apache / Mortalidad</Label>
|
||||
<Input
|
||||
placeholder="Valor Apache o riesgo"
|
||||
value={apache}
|
||||
onChange={(e) => setApache(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<Label>Derivado de</Label>
|
||||
<Input
|
||||
placeholder="Hospital o clínica de derivación"
|
||||
value={derivacion}
|
||||
onChange={(e) => setDerivacion(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
{/* Columna 2: Datos de Ingreso */}
|
||||
<div className="space-y-6">
|
||||
<Card>
|
||||
<CardContent className="pt-6 space-y-4">
|
||||
<h3 className="font-semibold text-gray-900 flex items-center gap-2">
|
||||
<ClipboardList className="h-4 w-4" />
|
||||
Datos de Ingreso
|
||||
</h3>
|
||||
|
||||
<div>
|
||||
<Label>Fecha Ingreso al Hospital</Label>
|
||||
<Input
|
||||
type="date"
|
||||
value={fechaIngresoHospital}
|
||||
onChange={(e) => setFechaIngresoHospital(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<Label>Fecha Ingreso a Clínica Médica</Label>
|
||||
<Input
|
||||
type="date"
|
||||
value={fechaIngresoClinica}
|
||||
onChange={(e) => setFechaIngresoClinica(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<Label>Apache / Mortalidad</Label>
|
||||
<Input
|
||||
placeholder="Valor Apache o riesgo"
|
||||
value={apache}
|
||||
onChange={(e) => setApache(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<Label>Derivado de</Label>
|
||||
<Input
|
||||
placeholder="Hospital o clínica de derivación"
|
||||
value={derivacion}
|
||||
onChange={(e) => setDerivacion(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card>
|
||||
<CardContent className="pt-6 space-y-4">
|
||||
<h3 className="font-semibold text-gray-900 flex items-center gap-2">
|
||||
<Stethoscope className="h-4 w-4" />
|
||||
Médico Ingresante
|
||||
</h3>
|
||||
|
||||
<div>
|
||||
<Label>Médico Ingresante *</Label>
|
||||
<Input
|
||||
placeholder="Nombre del médico"
|
||||
value={medico}
|
||||
onChange={(e) => setMedico(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
{/* Columna 3: Datos Clínicos */}
|
||||
<div className="space-y-6">
|
||||
<Card>
|
||||
<CardContent className="pt-6 space-y-4">
|
||||
<h3 className="font-semibold text-gray-900">
|
||||
Datos Clínicos
|
||||
</h3>
|
||||
|
||||
<div>
|
||||
<Label>Motivo de Consulta *</Label>
|
||||
<textarea
|
||||
className="w-full p-2 border rounded-md text-sm min-h-[80px]"
|
||||
placeholder="Motivo por el cual consulta"
|
||||
value={motivoConsulta}
|
||||
onChange={(e) => setMotivoConsulta(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Label>Diagnóstico de Ingreso *</Label>
|
||||
<textarea
|
||||
className="w-full p-2 border rounded-md text-sm min-h-[80px]"
|
||||
placeholder="Diagnóstico principal"
|
||||
value={diagnosticoIngreso}
|
||||
onChange={(e) => setDiagnosticoIngreso(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Label>Enfermedad Actual *</Label>
|
||||
<textarea
|
||||
className="w-full p-2 border rounded-md text-sm min-h-[100px]"
|
||||
placeholder="Descripción de la enfermedad actual"
|
||||
value={enfermedadActual}
|
||||
onChange={(e) => setEnfermedadActual(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Label>Antecedentes de la Enfermedad Actual</Label>
|
||||
<textarea
|
||||
className="w-full p-2 border rounded-md text-sm min-h-[80px]"
|
||||
placeholder="Antecedentes relevantes"
|
||||
value={antecedentesEnfermedadActual}
|
||||
onChange={(e) => setAntecedentesEnfermedadActual(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -58,6 +58,7 @@ interface HistoriaClinicaProps {
|
||||
onActualizarInternacion: (id: string, datos: Partial<Internacion>) => void;
|
||||
onActualizarCama: (id: string, datos: Partial<Cama>) => void;
|
||||
onVolver: () => void;
|
||||
onEditarIngreso?: () => void;
|
||||
}
|
||||
|
||||
const PARAMETROS_COMUNES: Record<string, { unidad: string; referencia: string }> = {
|
||||
@@ -154,11 +155,12 @@ export function HistoriaClinica({
|
||||
onActualizarInternacion,
|
||||
onActualizarCama,
|
||||
onVolver,
|
||||
onEditarIngreso,
|
||||
}: HistoriaClinicaProps) {
|
||||
const [tabActivo, setTabActivo] = useState('evoluciones');
|
||||
const [detalleExpandido, setDetalleExpandido] = useState(false);
|
||||
const [editIngresoDialog, setEditIngresoDialog] = useState(false);
|
||||
const [editFechaIngreso, setEditFechaIngreso] = useState(internacion.fechaIngreso);
|
||||
const [editFechaIngreso, setEditFechaIngreso] = useState(internacion.fechaIngresoClinica);
|
||||
const [editMotivoConsulta, setEditMotivoConsulta] = useState(internacion.motivoConsulta || '');
|
||||
const [editDiagnostico, setEditDiagnostico] = useState(internacion.diagnosticoIngreso || '');
|
||||
const [editEnfermedadActual, setEditEnfermedadActual] = useState(internacion.enfermedadActual || '');
|
||||
@@ -192,7 +194,7 @@ export function HistoriaClinica({
|
||||
onActualizarCama(editCamaId, { estado: 'Ocupada', pacienteId: paciente.id, internacionId: internacion.id });
|
||||
}
|
||||
onActualizarInternacion(internacion.id, {
|
||||
fechaIngreso: editFechaIngreso,
|
||||
fechaIngresoClinica: editFechaIngreso,
|
||||
motivoConsulta: editMotivoConsulta,
|
||||
diagnosticoIngreso: editDiagnostico,
|
||||
enfermedadActual: editEnfermedadActual,
|
||||
@@ -222,7 +224,7 @@ export function HistoriaClinica({
|
||||
<ArrowLeft className="h-4 w-4 mr-2" />
|
||||
Volver
|
||||
</Button>
|
||||
<Button variant="outline" onClick={() => setEditIngresoDialog(true)}>
|
||||
<Button variant="outline" onClick={onEditarIngreso}>
|
||||
<Pencil className="h-4 w-4 mr-2" />
|
||||
Editar Ingreso
|
||||
</Button>
|
||||
@@ -292,12 +294,12 @@ export function HistoriaClinica({
|
||||
|
||||
<div>
|
||||
<p className="text-gray-500">Antecedentes</p>
|
||||
<p className="font-medium truncate">{paciente.antecedentes || 'Sin registrados'}</p>
|
||||
<p className="font-medium truncate">{paciente.antecedentes || 'No constan'}</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<p className="text-gray-500">Fecha de Ingreso</p>
|
||||
<p className="font-medium">{formatDateDDMMYYYY(internacion.fechaIngreso)}</p>
|
||||
<p className="font-medium">{internacion.fechaIngresoClinica ? formatDateDDMMYYYY(internacion.fechaIngresoClinica) : 'N/A'}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-gray-500">Diagnóstico de Ingreso</p>
|
||||
@@ -305,8 +307,8 @@ export function HistoriaClinica({
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-gray-500">Días de Internación</p>
|
||||
<Badge className={calcularDiasInternado(internacion.fechaIngreso) > 7 ? 'bg-red-100 text-red-800' : 'bg-blue-100 text-blue-800'}>
|
||||
{calcularDiasInternado(internacion.fechaIngreso)} días
|
||||
<Badge className={internacion.fechaIngresoClinica && calcularDiasInternado(internacion.fechaIngresoClinica) > 7 ? 'bg-red-100 text-red-800' : 'bg-blue-100 text-blue-800'}>
|
||||
{internacion.fechaIngresoClinica ? calcularDiasInternado(internacion.fechaIngresoClinica) : 0} días
|
||||
</Badge>
|
||||
</div>
|
||||
|
||||
@@ -344,7 +346,7 @@ export function HistoriaClinica({
|
||||
const colorLightGray = rgb(0.92, 0.92, 0.92);
|
||||
|
||||
const edad = paciente.fechaNacimiento ? calcularEdad(paciente.fechaNacimiento) : '';
|
||||
const fechaFormateada = formatDateDDMMYYYY(internacion.fechaIngreso);
|
||||
const fechaFormateada = internacion.fechaIngresoClinica ? formatDateDDMMYYYY(internacion.fechaIngresoClinica) : 'N/A';
|
||||
|
||||
// Header
|
||||
page.drawRectangle({ x: 0, y: height - 80, width, height: 80, color: colorPrimary });
|
||||
|
||||
@@ -18,7 +18,7 @@ interface InternacionesProps {
|
||||
laboratorios?: Laboratorio[];
|
||||
cultivos?: Cultivo[];
|
||||
areas: Area[];
|
||||
onIniciarInternacion: (internacion: Omit<Internacion, 'id' | 'fechaIngreso' | 'activa'>) => void;
|
||||
onIniciarInternacion: (internacion: Omit<Internacion, 'id' | 'activa'>) => void;
|
||||
onFinalizarInternacion: (internacionId: string, datos: {
|
||||
fechaEgreso: string;
|
||||
diagnosticoEgreso: string;
|
||||
@@ -27,6 +27,7 @@ interface InternacionesProps {
|
||||
getPacienteById: (id: string) => Paciente | undefined;
|
||||
getCamaById: (id: string) => Cama | undefined;
|
||||
onVerHC?: (internacionId: string) => void;
|
||||
onNuevoIngreso?: () => void;
|
||||
}
|
||||
|
||||
export function Internaciones({
|
||||
@@ -39,6 +40,7 @@ export function Internaciones({
|
||||
getCamaById,
|
||||
areas,
|
||||
onVerHC,
|
||||
onNuevoIngreso,
|
||||
}: InternacionesProps) {
|
||||
const [filtroEstado, setFiltroEstado] = useState<'todas' | 'activas' | 'finalizadas'>('todas');
|
||||
const [dialogoNuevaAbierto, setDialogoNuevaAbierto] = useState(false);
|
||||
@@ -116,7 +118,7 @@ export function Internaciones({
|
||||
(filtroEstado === 'finalizadas' && !i.activa);
|
||||
|
||||
return cumpleBusqueda && cumpleEstado;
|
||||
}).sort((a, b) => new Date(b.fechaIngreso).getTime() - new Date(a.fechaIngreso).getTime());
|
||||
}).sort((a, b) => new Date(b.fechaIngresoClinica || '').getTime() - new Date(a.fechaIngresoClinica || '').getTime());
|
||||
|
||||
const pacientesSinInternar = pacientes.filter(p => {
|
||||
const internacionActiva = internaciones.find(i => i.pacienteId === p.id && i.activa);
|
||||
@@ -143,7 +145,7 @@ export function Internaciones({
|
||||
</div>
|
||||
<Dialog open={dialogoNuevaAbierto} onOpenChange={setDialogoNuevaAbierto}>
|
||||
<DialogTrigger asChild>
|
||||
<Button onClick={resetFormularioNueva}>
|
||||
<Button onClick={() => onNuevoIngreso ? onNuevoIngreso() : setDialogoNuevaAbierto(true)}>
|
||||
<Plus className="h-4 w-4 mr-2" />
|
||||
Nuevo Ingreso
|
||||
</Button>
|
||||
@@ -334,7 +336,7 @@ export function Internaciones({
|
||||
</div>
|
||||
<div className="flex items-center gap-2 text-gray-600">
|
||||
<Calendar className="h-4 w-4" />
|
||||
<span>Ingreso: {formatDateDDMMYYYY(internacion.fechaIngreso)}</span>
|
||||
<span>Ingreso: {internacion.fechaIngresoClinica ? formatDateDDMMYYYY(internacion.fechaIngresoClinica) : 'N/A'}</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2 text-gray-600">
|
||||
<Stethoscope className="h-4 w-4" />
|
||||
|
||||
@@ -18,7 +18,7 @@ interface MapaCamasProps {
|
||||
onActualizarCama: (id: string, datos: Partial<Cama>) => void;
|
||||
onAgregarCama: (cama: Omit<Cama, 'id'>) => string;
|
||||
onEliminarCama: (id: string) => void;
|
||||
onIniciarInternacion: (internacion: Omit<Internacion, 'id' | 'fechaIngreso' | 'activa'>) => void;
|
||||
onIniciarInternacion: (internacion: Omit<Internacion, 'id' | 'activa'>) => void;
|
||||
onAgregarArea: (area: Omit<Area, 'id'>) => string;
|
||||
onActualizarArea: (id: string, datos: Partial<Area>) => void;
|
||||
onEliminarArea: (id: string) => void;
|
||||
@@ -332,7 +332,7 @@ export function MapaCamas({
|
||||
<p className="font-medium">Paciente:</p>
|
||||
<p className="text-lg">{paciente.apellido}, {paciente.nombre}</p>
|
||||
<p className="text-sm text-gray-500">DNI: {paciente.dni}</p>
|
||||
<p className="text-sm text-gray-500">Ingreso: {formatDateDDMMYYYY(internacion.fechaIngreso)}</p>
|
||||
<p className="text-sm text-gray-500">Ingreso: {internacion.fechaIngresoClinica ? formatDateDDMMYYYY(internacion.fechaIngresoClinica) : 'N/A'}</p>
|
||||
<p className="text-sm text-gray-500">Diagnóstico: {internacion.diagnosticoIngreso}</p>
|
||||
<p className="text-sm text-gray-500">Médico: {internacion.medicoIngresante}</p>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,310 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { ArrowLeft, Save, X, Bed, Calendar, Stethoscope, User, ClipboardList } from 'lucide-react';
|
||||
import { Card, CardContent } from '@/components/ui/card';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Label } from '@/components/ui/label';
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import type { Paciente, Cama, Area, Internacion } from '@/types';
|
||||
|
||||
interface NuevoIngresoProps {
|
||||
pacientes: Paciente[];
|
||||
camas: Cama[];
|
||||
areas: Area[];
|
||||
onIniciarInternacion: (internacion: Omit<Internacion, 'id' | 'activa'>) => void;
|
||||
onVolver: () => void;
|
||||
getAreaName: (areaId: string | undefined) => string;
|
||||
}
|
||||
|
||||
export function NuevoIngreso({ pacientes, camas, areas, onIniciarInternacion, onVolver, getAreaName }: NuevoIngresoProps) {
|
||||
const [pacienteSeleccionado, setPacienteSeleccionado] = useState<string>('');
|
||||
const [busqueda, setBusqueda] = useState('');
|
||||
const [camaSeleccionada, setCamaSeleccionada] = useState('');
|
||||
const [medico, setMedico] = useState('');
|
||||
const [fechaIngresoHospital, setFechaIngresoHospital] = useState('');
|
||||
const [fechaIngresoClinica, setFechaIngresoClinica] = useState('');
|
||||
const [motivoConsulta, setMotivoConsulta] = useState('');
|
||||
const [diagnosticoIngreso, setDiagnosticoIngreso] = useState('');
|
||||
const [enfermedadActual, setEnfermedadActual] = useState('');
|
||||
const [antecedentesEnfermedadActual, setAntecedentesEnfermedadActual] = useState('');
|
||||
const [apache, setApache] = useState('');
|
||||
const [derivacion, setDerivacion] = useState('');
|
||||
|
||||
const resetFormulario = () => {
|
||||
setPacienteSeleccionado('');
|
||||
setBusqueda('');
|
||||
setCamaSeleccionada('');
|
||||
setMedico('');
|
||||
setFechaIngresoHospital('');
|
||||
setFechaIngresoClinica('');
|
||||
setMotivoConsulta('');
|
||||
setDiagnosticoIngreso('');
|
||||
setEnfermedadActual('');
|
||||
setAntecedentesEnfermedadActual('');
|
||||
setApache('');
|
||||
setDerivacion('');
|
||||
};
|
||||
|
||||
const pacientesSinInternar = pacientes;
|
||||
|
||||
const handleSubmit = () => {
|
||||
if (!pacienteSeleccionado || !camaSeleccionada || !medico || !diagnosticoIngreso || !enfermedadActual) {
|
||||
alert('Por favor complete los campos obligatorios');
|
||||
return;
|
||||
}
|
||||
|
||||
onIniciarInternacion({
|
||||
pacienteId: pacienteSeleccionado,
|
||||
camaId: camaSeleccionada,
|
||||
medicoIngresante: medico,
|
||||
diagnosticoIngreso,
|
||||
motivoConsulta: motivoConsulta || undefined,
|
||||
enfermedadActual,
|
||||
antecedentesEnfermedadActual: antecedentesEnfermedadActual || undefined,
|
||||
fechaIngresoHospital: fechaIngresoHospital || undefined,
|
||||
fechaIngresoClinica: fechaIngresoClinica || undefined,
|
||||
apache: apache || undefined,
|
||||
derivacion: derivacion || undefined,
|
||||
});
|
||||
|
||||
resetFormulario();
|
||||
onVolver();
|
||||
};
|
||||
|
||||
const selectedPaciente = pacientes.find(p => p.id === pacienteSeleccionado);
|
||||
const camasDisponibles = camaSeleccionada
|
||||
? []
|
||||
: camas.filter(c => c.estado === 'Disponible' && !c.pacienteId);
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-4">
|
||||
<Button variant="ghost" size="icon" onClick={onVolver}>
|
||||
<ArrowLeft className="h-5 w-5" />
|
||||
</Button>
|
||||
<div>
|
||||
<h1 className="text-2xl font-bold text-gray-900 flex items-center gap-2">
|
||||
<User className="h-6 w-6 text-blue-600" />
|
||||
Nuevo Ingreso
|
||||
</h1>
|
||||
<p className="text-gray-500">Formulario de internación</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex gap-2">
|
||||
<Button variant="outline" onClick={() => { resetFormulario(); onVolver(); }}>
|
||||
<X className="h-4 w-4 mr-2" />
|
||||
Cancelar
|
||||
</Button>
|
||||
<Button className="bg-blue-600 hover:bg-blue-700" onClick={handleSubmit}>
|
||||
<Save className="h-4 w-4 mr-2" />
|
||||
Guardar Ingreso
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
|
||||
{/* Columna 1: Datos del Paciente, Cama y Datos Adicionales */}
|
||||
<div className="space-y-6">
|
||||
<Card>
|
||||
<CardContent className="pt-6 space-y-4">
|
||||
<h3 className="font-semibold text-gray-900 flex items-center gap-2">
|
||||
<User className="h-4 w-4" />
|
||||
Datos del Paciente
|
||||
</h3>
|
||||
|
||||
{!pacienteSeleccionado ? (
|
||||
<>
|
||||
<Label>Buscar Paciente *</Label>
|
||||
<Input
|
||||
placeholder="Apellido, nombre o DNI"
|
||||
value={busqueda}
|
||||
onChange={(e) => setBusqueda(e.target.value)}
|
||||
/>
|
||||
<div className="max-h-48 overflow-auto border rounded-md">
|
||||
{(() => {
|
||||
const q = busqueda.trim().toLowerCase();
|
||||
if (!q) {
|
||||
return <div className="p-2 text-sm text-gray-500">Escriba para buscar</div>;
|
||||
}
|
||||
const matches = pacientesSinInternar.filter(p =>
|
||||
p.apellido.toLowerCase().includes(q) ||
|
||||
p.nombre.toLowerCase().includes(q) ||
|
||||
p.dni.includes(q)
|
||||
);
|
||||
if (matches.length === 0) {
|
||||
return <div className="p-2 text-sm text-gray-500">Sin resultados</div>;
|
||||
}
|
||||
return matches.map(p => (
|
||||
<button
|
||||
key={p.id}
|
||||
type="button"
|
||||
onClick={() => setPacienteSeleccionado(p.id)}
|
||||
className="w-full text-left p-2 hover:bg-gray-50 text-sm"
|
||||
>
|
||||
{p.apellido}, {p.nombre} — DNI: {p.dni}
|
||||
</button>
|
||||
));
|
||||
})()}
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<div className="flex items-center gap-2 flex-wrap">
|
||||
<Badge className="bg-blue-100 text-blue-800">
|
||||
{selectedPaciente?.apellido}, {selectedPaciente?.nombre}
|
||||
</Badge>
|
||||
<Badge variant="outline">DNI: {selectedPaciente?.dni}</Badge>
|
||||
<Button size="sm" variant="ghost" onClick={() => { setPacienteSeleccionado(''); setBusqueda(''); }}>
|
||||
Cambiar
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card>
|
||||
<CardContent className="pt-6 space-y-4">
|
||||
<h3 className="font-semibold text-gray-900 flex items-center gap-2">
|
||||
<Bed className="h-4 w-4" />
|
||||
Asignación de Cama
|
||||
</h3>
|
||||
|
||||
<div>
|
||||
<Label>Cama *</Label>
|
||||
<Select value={camaSeleccionada} onValueChange={setCamaSeleccionada}>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="Seleccionar cama" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{camas.map(c => (
|
||||
<SelectItem key={c.id} value={c.id}>
|
||||
{c.numero} - {getAreaName(c.areaId)} ({c.tipo})
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
{/* Columna 2: Fechas y Médico */}
|
||||
<div className="space-y-6">
|
||||
<Card>
|
||||
<CardContent className="pt-6 space-y-4">
|
||||
<h3 className="font-semibold text-gray-900 flex items-center gap-2">
|
||||
<Calendar className="h-4 w-4" />
|
||||
Datos de Ingreso
|
||||
</h3>
|
||||
|
||||
<div>
|
||||
<Label>Fecha Ingreso al Hospital</Label>
|
||||
<Input
|
||||
type="date"
|
||||
value={fechaIngresoHospital}
|
||||
onChange={(e) => setFechaIngresoHospital(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<Label>Fecha Ingreso a Clínica Médica</Label>
|
||||
<Input
|
||||
type="date"
|
||||
value={fechaIngresoClinica}
|
||||
onChange={(e) => setFechaIngresoClinica(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<Label>Apache / Mortalidad</Label>
|
||||
<Input
|
||||
placeholder="Valor Apache o riesgo"
|
||||
value={apache}
|
||||
onChange={(e) => setApache(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<Label>Derivado de</Label>
|
||||
<Input
|
||||
placeholder="Hospital o clínica de derivación"
|
||||
value={derivacion}
|
||||
onChange={(e) => setDerivacion(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card>
|
||||
<CardContent className="pt-6 space-y-4">
|
||||
<h3 className="font-semibold text-gray-900 flex items-center gap-2">
|
||||
<Stethoscope className="h-4 w-4" />
|
||||
Médico Ingresante
|
||||
</h3>
|
||||
|
||||
<div>
|
||||
<Label>Médico Ingresante *</Label>
|
||||
<Input
|
||||
placeholder="Nombre del médico"
|
||||
value={medico}
|
||||
onChange={(e) => setMedico(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
{/* Columna 3: Datos Clínicos */}
|
||||
<div className="space-y-6">
|
||||
<Card>
|
||||
<CardContent className="pt-6 space-y-4">
|
||||
<h3 className="font-semibold text-gray-900">
|
||||
Datos Clínicos
|
||||
</h3>
|
||||
|
||||
<div>
|
||||
<Label>Motivo de Consulta *</Label>
|
||||
<textarea
|
||||
className="w-full p-2 border rounded-md text-sm min-h-[80px]"
|
||||
placeholder="Motivo por el cual consulta"
|
||||
value={motivoConsulta}
|
||||
onChange={(e) => setMotivoConsulta(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Label>Diagnóstico de Ingreso *</Label>
|
||||
<textarea
|
||||
className="w-full p-2 border rounded-md text-sm min-h-[80px]"
|
||||
placeholder="Diagnóstico principal"
|
||||
value={diagnosticoIngreso}
|
||||
onChange={(e) => setDiagnosticoIngreso(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Label>Enfermedad Actual *</Label>
|
||||
<textarea
|
||||
className="w-full p-2 border rounded-md text-sm min-h-[100px]"
|
||||
placeholder="Descripción de la enfermedad actual"
|
||||
value={enfermedadActual}
|
||||
onChange={(e) => setEnfermedadActual(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Label>Antecedentes de la Enfermedad Actual</Label>
|
||||
<textarea
|
||||
className="w-full p-2 border rounded-md text-sm min-h-[80px]"
|
||||
placeholder="Antecedentes relevantes"
|
||||
value={antecedentesEnfermedadActual}
|
||||
onChange={(e) => setAntecedentesEnfermedadActual(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
+5
-2
@@ -35,7 +35,8 @@ export interface Internacion {
|
||||
id: string;
|
||||
pacienteId: string;
|
||||
camaId: string;
|
||||
fechaIngreso: string;
|
||||
fechaIngresoHospital?: string;
|
||||
fechaIngresoClinica?: string;
|
||||
fechaEgreso?: string;
|
||||
diagnosticoIngreso: string;
|
||||
motivoConsulta?: string;
|
||||
@@ -45,6 +46,8 @@ export interface Internacion {
|
||||
medicoIngresante: string;
|
||||
motivoEgreso?: 'Alta médica' | 'Alta voluntaria' | 'Derivación' | 'Fallecimiento' | 'Otro';
|
||||
activa: boolean;
|
||||
apache?: string;
|
||||
derivacion?: string;
|
||||
}
|
||||
|
||||
export interface ExamenFisico {
|
||||
@@ -133,4 +136,4 @@ export interface Cultivo {
|
||||
observaciones?: string;
|
||||
}
|
||||
|
||||
export type Vista = 'dashboard' | 'camas' | 'pacientes' | 'internaciones' | 'evoluciones' | 'laboratorios' | 'acidobase' | 'cultivos' | 'historiaclinica';
|
||||
export type Vista = 'dashboard' | 'camas' | 'pacientes' | 'internaciones' | 'evoluciones' | 'laboratorios' | 'acidobase' | 'cultivos' | 'historiaclinica' | 'nuevoingreso' | 'editaringreso';
|
||||
|
||||
Reference in New Issue
Block a user