Agregar tabla interconsultas: DB, store, UI con CRUD completo

This commit is contained in:
2026-04-17 10:00:10 -03:00
parent 59471d65bc
commit 4510938991
6 changed files with 983 additions and 5 deletions
+161 -5
View File
@@ -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>
);
}