Agregar tabla interconsultas: DB, store, UI con CRUD completo
This commit is contained in:
@@ -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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user