Agregar tabla interconsultas: DB, store, UI con CRUD completo
This commit is contained in:
@@ -144,6 +144,7 @@ function AppContent() {
|
||||
acidosBase={store.getAcidosBaseByPaciente(paciente.id)}
|
||||
cultivos={store.getCultivosByPaciente(paciente.id)}
|
||||
estudiosComplementarios={store.estudiosComplementarios.filter(e => e.pacienteId === paciente.id)}
|
||||
interconsultas={store.interconsultas.filter(ic => ic.internacionId === internacion.id)}
|
||||
onAgregarEvolucion={store.agregarEvolucion}
|
||||
onActualizarEvolucion={store.actualizarEvolucion}
|
||||
onEliminarEvolucion={store.eliminarEvolucion}
|
||||
@@ -159,6 +160,9 @@ function AppContent() {
|
||||
onAgregarEstudioComplementario={(e) => store.agregarEstudioComplementario({ ...e, internacionId: internacion.id })}
|
||||
onActualizarEstudioComplementario={store.actualizarEstudioComplementario}
|
||||
onEliminarEstudioComplementario={store.eliminarEstudioComplementario}
|
||||
onAgregarInterconsulta={(ic) => store.agregarInterconsulta({ ...ic, internacionId: internacion.id })}
|
||||
onActualizarInterconsulta={store.actualizarInterconsulta}
|
||||
onEliminarInterconsulta={store.eliminarInterconsulta}
|
||||
onActualizarInternacion={store.actualizarInternacion}
|
||||
onActualizarCama={store.actualizarCama}
|
||||
onVolver={() => store.setVista('internaciones')}
|
||||
|
||||
@@ -9,6 +9,7 @@ import type {
|
||||
AcidoBase,
|
||||
Cultivo,
|
||||
EstudioComplementario,
|
||||
Interconsulta,
|
||||
Vista
|
||||
} from '@/types';
|
||||
|
||||
@@ -35,6 +36,7 @@ interface HospitalState {
|
||||
acidosBase: AcidoBase[];
|
||||
cultivos: Cultivo[];
|
||||
estudiosComplementarios: EstudioComplementario[];
|
||||
interconsultas: Interconsulta[];
|
||||
vistaActual: Vista;
|
||||
currentInternacionId?: string | null;
|
||||
}
|
||||
@@ -51,6 +53,7 @@ const defaultState = (): HospitalState => ({
|
||||
acidosBase: [],
|
||||
cultivos: [],
|
||||
estudiosComplementarios: [],
|
||||
interconsultas: [],
|
||||
vistaActual: 'dashboard',
|
||||
camas: []
|
||||
});
|
||||
@@ -349,6 +352,30 @@ const finalizarInternacion = useCallback((internacionId: string, datos: {
|
||||
}));
|
||||
}, []);
|
||||
|
||||
// Acciones de interconsultas
|
||||
const agregarInterconsulta = useCallback((interconsulta: Omit<Interconsulta, 'id'>) => {
|
||||
const nuevaInterconsulta: Interconsulta = { ...interconsulta, id: generateUUID() };
|
||||
setState(prev => ({
|
||||
...prev,
|
||||
interconsultas: [...prev.interconsultas, nuevaInterconsulta],
|
||||
}));
|
||||
return nuevaInterconsulta.id;
|
||||
}, []);
|
||||
|
||||
const actualizarInterconsulta = useCallback((id: string, datos: Partial<Interconsulta>) => {
|
||||
setState(prev => ({
|
||||
...prev,
|
||||
interconsultas: prev.interconsultas.map(ic => ic.id === id ? { ...ic, ...datos } : ic),
|
||||
}));
|
||||
}, []);
|
||||
|
||||
const eliminarInterconsulta = useCallback((id: string) => {
|
||||
setState(prev => ({
|
||||
...prev,
|
||||
interconsultas: prev.interconsultas.filter(ic => ic.id !== id),
|
||||
}));
|
||||
}, []);
|
||||
|
||||
// Acciones de areas
|
||||
const agregarArea = useCallback((area: Omit<Area, 'id'>) => {
|
||||
const nuevaArea: Area = { ...area, id: generateUUID() };
|
||||
@@ -472,6 +499,9 @@ const getEstadisticas = useCallback(() => {
|
||||
agregarEstudioComplementario,
|
||||
actualizarEstudioComplementario,
|
||||
eliminarEstudioComplementario,
|
||||
agregarInterconsulta,
|
||||
actualizarInterconsulta,
|
||||
eliminarInterconsulta,
|
||||
agregarArea,
|
||||
actualizarArea,
|
||||
eliminarArea,
|
||||
|
||||
@@ -50,7 +50,7 @@ import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
|
||||
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table';
|
||||
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from '@/components/ui/dropdown-menu';
|
||||
import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer } from 'recharts';
|
||||
import type { Laboratorio, AcidoBase, Cultivo, Paciente, ResultadoLaboratorio, Internacion, Evolucion, Cama, EstudioComplementario } from '@/types';
|
||||
import type { Laboratorio, AcidoBase, Cultivo, Paciente, ResultadoLaboratorio, Internacion, Evolucion, Cama, EstudioComplementario, Interconsulta } from '@/types';
|
||||
import { formatDateDDMMYYYY } from '@/lib/utils';
|
||||
|
||||
interface HistoriaClinicaProps {
|
||||
@@ -63,6 +63,7 @@ interface HistoriaClinicaProps {
|
||||
acidosBase: AcidoBase[];
|
||||
cultivos: Cultivo[];
|
||||
estudiosComplementarios: EstudioComplementario[];
|
||||
interconsultas: Interconsulta[];
|
||||
onAgregarEvolucion: (evolucion: Omit<Evolucion, 'id'>) => void;
|
||||
onActualizarEvolucion: (id: string, datos: Partial<Evolucion>) => void;
|
||||
onEliminarEvolucion: (id: string) => void;
|
||||
@@ -79,6 +80,9 @@ interface HistoriaClinicaProps {
|
||||
onAgregarEstudioComplementario: (estudio: Omit<EstudioComplementario, 'id'>) => void;
|
||||
onActualizarEstudioComplementario: (id: string, datos: Partial<EstudioComplementario>) => void;
|
||||
onEliminarEstudioComplementario: (id: string) => void;
|
||||
onAgregarInterconsulta: (interconsulta: Omit<Interconsulta, 'id'>) => void;
|
||||
onActualizarInterconsulta: (id: string, datos: Partial<Interconsulta>) => void;
|
||||
onEliminarInterconsulta: (id: string) => void;
|
||||
onActualizarInternacion: (id: string, datos: Partial<Internacion>) => void;
|
||||
onActualizarCama: (id: string, datos: Partial<Cama>) => void;
|
||||
onVolver: () => void;
|
||||
@@ -168,6 +172,7 @@ export function HistoriaClinica({
|
||||
acidosBase,
|
||||
cultivos,
|
||||
estudiosComplementarios,
|
||||
interconsultas,
|
||||
onAgregarEvolucion,
|
||||
onActualizarEvolucion,
|
||||
onEliminarEvolucion,
|
||||
@@ -183,6 +188,9 @@ export function HistoriaClinica({
|
||||
onAgregarEstudioComplementario,
|
||||
onActualizarEstudioComplementario,
|
||||
onEliminarEstudioComplementario,
|
||||
onAgregarInterconsulta,
|
||||
onActualizarInterconsulta,
|
||||
onEliminarInterconsulta,
|
||||
onActualizarInternacion,
|
||||
onActualizarCama,
|
||||
onVolver,
|
||||
@@ -538,10 +546,14 @@ export function HistoriaClinica({
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="interconsultas" className="mt-4">
|
||||
<div className="text-center py-8 text-gray-400">
|
||||
<Users className="h-12 w-12 mx-auto mb-2 opacity-50" />
|
||||
<p>Sección Interconsultas en desarrollo</p>
|
||||
</div>
|
||||
<SeccionInterconsultas
|
||||
interconsultas={interconsultas as Interconsulta[]}
|
||||
pacienteId={paciente.id}
|
||||
internacionId={internacion.id}
|
||||
add={onAgregarInterconsulta}
|
||||
update={onActualizarInterconsulta}
|
||||
del={onEliminarInterconsulta}
|
||||
/>
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
</div>
|
||||
@@ -2006,4 +2018,148 @@ function SeccionEstudiosComplementarios({ estudios, internacionId, pacienteId, a
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function SeccionInterconsultas({ interconsultas, pacienteId, internacionId, add, update, del }: {
|
||||
interconsultas: Interconsulta[];
|
||||
pacienteId: string;
|
||||
internacionId: string;
|
||||
add: (ic: Omit<Interconsulta, 'id'>) => void;
|
||||
update: (id: string, datos: Partial<Interconsulta>) => void;
|
||||
del: (id: string) => void;
|
||||
}) {
|
||||
const [dialog, setDialog] = useState(false);
|
||||
const [fecha, setFecha] = useState(new Date().toISOString().split('T')[0]);
|
||||
const [servicioInterconsultado, setServicioInterconsultado] = useState('');
|
||||
const [realizada, setRealizada] = useState(false);
|
||||
const [respuestaInterconsulta, setRespuestaInterconsulta] = useState('');
|
||||
const [edit, setEdit] = useState<Interconsulta | null>(null);
|
||||
|
||||
const reset = () => {
|
||||
setFecha(new Date().toISOString().split('T')[0]);
|
||||
setServicioInterconsultado('');
|
||||
setRealizada(false);
|
||||
setRespuestaInterconsulta('');
|
||||
setEdit(null);
|
||||
};
|
||||
|
||||
const handleGuardar = () => {
|
||||
if (!servicioInterconsultado) return;
|
||||
if (edit) {
|
||||
update(edit.id, { fecha, servicioInterconsultado, realizada, respuestaInterconsulta });
|
||||
} else {
|
||||
add({ pacienteId, internacionId, fecha, servicioInterconsultado, realizada, respuestaInterconsulta });
|
||||
}
|
||||
setDialog(false);
|
||||
reset();
|
||||
};
|
||||
|
||||
const loadEdit = (ic: Interconsulta) => {
|
||||
setEdit(ic);
|
||||
setFecha(ic.fecha);
|
||||
setServicioInterconsultado(ic.servicioInterconsultado);
|
||||
setRealizada(ic.realizada);
|
||||
setRespuestaInterconsulta(ic.respuestaInterconsulta || '');
|
||||
setDialog(true);
|
||||
};
|
||||
|
||||
const icsFiltrados = interconsultas.filter(ic => ic.internacionId === internacionId);
|
||||
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<div className="flex justify-between items-center">
|
||||
<Button variant="outline" onClick={() => { reset(); setDialog(true); }}>
|
||||
<Plus className="h-4 w-4 mr-2" />
|
||||
Nueva IC
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<Dialog open={dialog} onOpenChange={setDialog}>
|
||||
<DialogContent className="sm:max-w-lg">
|
||||
<DialogHeader>
|
||||
<DialogTitle>{edit ? 'Editar' : 'Nueva'} Interconsulta</DialogTitle>
|
||||
</DialogHeader>
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<Label>Fecha</Label>
|
||||
<Input type="date" value={fecha} onChange={e => setFecha(e.target.value)} />
|
||||
</div>
|
||||
<div>
|
||||
<Label>Servicio Interconsultado</Label>
|
||||
<Input
|
||||
value={servicioInterconsultado}
|
||||
onChange={e => setServicioInterconsultado(e.target.value)}
|
||||
placeholder="Ej: Cardiología, Neurología, Infectología, etc."
|
||||
/>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<input
|
||||
type="checkbox"
|
||||
id="realizada"
|
||||
checked={realizada}
|
||||
onChange={e => setRealizada(e.target.checked)}
|
||||
className="w-4 h-4"
|
||||
/>
|
||||
<Label htmlFor="realizada">Realizada</Label>
|
||||
</div>
|
||||
<div>
|
||||
<Label>Respuesta</Label>
|
||||
<textarea
|
||||
className="w-full p-2 border rounded-md text-sm min-h-[100px]"
|
||||
value={respuestaInterconsulta}
|
||||
onChange={e => setRespuestaInterconsulta(e.target.value)}
|
||||
placeholder="Ingrese la respuesta de la interconsulta..."
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex justify-end gap-2 mt-4">
|
||||
<Button variant="outline" onClick={() => setDialog(false)}>Cancelar</Button>
|
||||
<Button onClick={handleGuardar} disabled={!servicioInterconsultado}>
|
||||
<Save className="h-4 w-4 mr-2" />
|
||||
Guardar
|
||||
</Button>
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
|
||||
{icsFiltrados.length === 0 ? (
|
||||
<div className="text-center py-8 text-gray-400">
|
||||
<Users className="h-12 w-12 mx-auto mb-2 opacity-50" />
|
||||
<p>No hay interconsultas</p>
|
||||
</div>
|
||||
) : (
|
||||
<div className="space-y-3">
|
||||
{icsFiltrados.sort((a, b) => new Date(b.fecha).getTime() - new Date(a.fecha).getTime()).map(ic => (
|
||||
<Card key={ic.id} className="hover:shadow-md transition-shadow">
|
||||
<CardContent className="p-4">
|
||||
<div className="flex flex-col sm:flex-row sm:items-start sm:justify-between gap-3">
|
||||
<div className="flex-1">
|
||||
<div className="flex items-center gap-2 mb-2">
|
||||
<Calendar className="h-4 w-4 text-gray-500" />
|
||||
<span className="text-sm font-medium">{ic.fecha}</span>
|
||||
{ic.realizada && (
|
||||
<Badge variant="secondary" className="ml-2">Realizada</Badge>
|
||||
)}
|
||||
</div>
|
||||
<h4 className="font-semibold text-gray-900 dark:text-white">{ic.servicioInterconsultado}</h4>
|
||||
{ic.respuestaInterconsulta && (
|
||||
<p className="text-sm text-gray-600 dark:text-gray-300 mt-1 whitespace-pre-wrap">{ic.respuestaInterconsulta}</p>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex gap-1 self-start">
|
||||
<Button size="sm" variant="outline" onClick={() => loadEdit(ic)}>
|
||||
<Pencil className="h-4 w-4" />
|
||||
</Button>
|
||||
<Button size="sm" variant="outline" className="text-red-600" onClick={() => del(ic.id)}>
|
||||
<Trash2 className="h-4 w-4" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -152,4 +152,14 @@ export interface EstudioComplementario {
|
||||
resultado: string;
|
||||
}
|
||||
|
||||
export interface Interconsulta {
|
||||
id: string;
|
||||
pacienteId: string;
|
||||
internacionId: string;
|
||||
fecha: string;
|
||||
servicioInterconsultado: string;
|
||||
realizada: boolean;
|
||||
respuestaInterconsulta?: string;
|
||||
}
|
||||
|
||||
export type Vista = 'dashboard' | 'camas' | 'pacientes' | 'internaciones' | 'evoluciones' | 'laboratorios' | 'acidobase' | 'cultivos' | 'historiaclinica' | 'nuevoingreso' | 'editaringreso';
|
||||
|
||||
Reference in New Issue
Block a user