Agregar estudios complementarios en HC
- Agregar tabla estudiosComplementarios en SQLite y API - Agregar interface EstudioComplementario en tipos - Agregar funciones en store para gestionar estudios - Nuevo tab en HistoriaClinica con filtro por internacion/todos - Fix tema dark mode (colores black en lugar de blue)
This commit is contained in:
@@ -39,7 +39,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 } from '@/types';
|
||||
import type { Laboratorio, AcidoBase, Cultivo, Paciente, ResultadoLaboratorio, Internacion, Evolucion, Cama, EstudioComplementario } from '@/types';
|
||||
import { formatDateDDMMYYYY } from '@/lib/utils';
|
||||
|
||||
interface HistoriaClinicaProps {
|
||||
@@ -51,6 +51,7 @@ interface HistoriaClinicaProps {
|
||||
laboratorios: Laboratorio[];
|
||||
acidosBase: AcidoBase[];
|
||||
cultivos: Cultivo[];
|
||||
estudiosComplementarios: EstudioComplementario[];
|
||||
onAgregarEvolucion: (evolucion: Omit<Evolucion, 'id'>) => void;
|
||||
onActualizarEvolucion: (id: string, datos: Partial<Evolucion>) => void;
|
||||
onEliminarEvolucion: (id: string) => void;
|
||||
@@ -62,6 +63,8 @@ interface HistoriaClinicaProps {
|
||||
onAgregarCultivo: (cultivo: Omit<Cultivo, 'id'>) => void;
|
||||
onActualizarCultivo: (id: string, datos: Partial<Cultivo>) => void;
|
||||
onEliminarCultivo: (id: string) => void;
|
||||
onAgregarEstudioComplementario: (estudio: Omit<EstudioComplementario, 'id'>) => void;
|
||||
onEliminarEstudioComplementario: (id: string) => void;
|
||||
onActualizarInternacion: (id: string, datos: Partial<Internacion>) => void;
|
||||
onActualizarCama: (id: string, datos: Partial<Cama>) => void;
|
||||
onVolver: () => void;
|
||||
@@ -148,6 +151,7 @@ export function HistoriaClinica({
|
||||
laboratorios,
|
||||
acidosBase,
|
||||
cultivos,
|
||||
estudiosComplementarios,
|
||||
onAgregarEvolucion,
|
||||
onActualizarEvolucion,
|
||||
onEliminarEvolucion,
|
||||
@@ -159,6 +163,8 @@ export function HistoriaClinica({
|
||||
onAgregarCultivo,
|
||||
onActualizarCultivo,
|
||||
onEliminarCultivo,
|
||||
onAgregarEstudioComplementario,
|
||||
onEliminarEstudioComplementario,
|
||||
onActualizarInternacion,
|
||||
onActualizarCama,
|
||||
onVolver,
|
||||
@@ -509,6 +515,12 @@ export function HistoriaClinica({
|
||||
<span className="sm:hidden">Cult</span>
|
||||
({cultivos.length})
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="estudios" className="flex-shrink-0 flex items-center gap-1 text-xs sm:text-sm">
|
||||
<ClipboardList className="h-3 w-3 sm:h-4 sm:w-4" />
|
||||
<span className="hidden sm:inline">Estudios</span>
|
||||
<span className="sm:hidden">Est</span>
|
||||
({estudiosComplementarios.length})
|
||||
</TabsTrigger>
|
||||
</TabsList>
|
||||
|
||||
<TabsContent value="laboratorios" className="mt-4">
|
||||
@@ -526,6 +538,16 @@ export function HistoriaClinica({
|
||||
<TabsContent value="cultivos" className="mt-4">
|
||||
<SeccionCultivos cults={cultivos} patient={paciente} add={onAgregarCultivo} update={onActualizarCultivo} del={onEliminarCultivo} />
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="estudios" className="mt-4">
|
||||
<SeccionEstudiosComplementarios
|
||||
estudios={estudiosComplementarios}
|
||||
internacionId={internacion.id}
|
||||
pacienteId={paciente.id}
|
||||
add={onAgregarEstudioComplementario}
|
||||
del={onEliminarEstudioComplementario}
|
||||
/>
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
</div>
|
||||
);
|
||||
@@ -1011,10 +1033,13 @@ if (!esUnidadSiguiente) {
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
|
||||
{labs.length === 0 ? <div className="text-center py-8 text-gray-400"><FlaskConical className="h-12 w-12 mx-auto mb-2 opacity-50" /><p>No hay laboratorios</p></div> :
|
||||
<div className="overflow-x-auto min-w-full">
|
||||
<Table>
|
||||
<TableHeader>
|
||||
{labs.length === 0 ? (
|
||||
<div className="text-center py-8 text-gray-400"><FlaskConical className="h-12 w-12 mx-auto mb-2 opacity-50" /><p>No hay laboratorios</p></div>
|
||||
) : (
|
||||
<div className="relative w-full overflow-auto grid grid-cols-1">
|
||||
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead>Fecha</TableHead>
|
||||
<TableHead>Hto</TableHead>
|
||||
@@ -1085,8 +1110,9 @@ if (!esUnidadSiguiente) {
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1657,4 +1683,130 @@ const resetRes = () => {
|
||||
}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function SeccionEstudiosComplementarios({ estudios, internacionId, pacienteId, add, del }: {
|
||||
estudios: EstudioComplementario[];
|
||||
internacionId: string;
|
||||
pacienteId: string;
|
||||
add: (e: Omit<EstudioComplementario, 'id'>) => void;
|
||||
del: (id: string) => void;
|
||||
}) {
|
||||
const [dialog, setDialog] = useState(false);
|
||||
const [filtro, setFiltro] = useState<'todos' | 'internacion'>('internacion');
|
||||
const [fecha, setFecha] = useState(new Date().toISOString().split('T')[0]);
|
||||
const [tipo, setTipo] = useState('');
|
||||
const [resultado, setResultado] = useState('');
|
||||
|
||||
const estudiosFiltrados = filtro === 'internacion'
|
||||
? estudios.filter(e => e.internacionId === internacionId)
|
||||
: estudios;
|
||||
|
||||
const reset = () => {
|
||||
setFecha(new Date().toISOString().split('T')[0]);
|
||||
setTipo('');
|
||||
setResultado('');
|
||||
};
|
||||
|
||||
const handleGuardar = () => {
|
||||
if (!tipo || !resultado) return;
|
||||
add({
|
||||
pacienteId,
|
||||
internacionId,
|
||||
fecha,
|
||||
tipo,
|
||||
resultado,
|
||||
});
|
||||
setDialog(false);
|
||||
reset();
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<div className="flex flex-col sm:flex-row justify-between items-start sm:items-center gap-4">
|
||||
<div className="flex gap-2">
|
||||
<Button variant="outline" onClick={() => { reset(); setDialog(true); }}>
|
||||
<Plus className="h-4 w-4 mr-2" />
|
||||
Nuevo Estudio
|
||||
</Button>
|
||||
</div>
|
||||
<Select value={filtro} onValueChange={(v: 'todos' | 'internacion') => setFiltro(v)}>
|
||||
<SelectTrigger className="w-[180px]">
|
||||
<SelectValue placeholder="Filtrar" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="internacion">Solo esta internación</SelectItem>
|
||||
<SelectItem value="todos">Todos los estudios</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
<Dialog open={dialog} onOpenChange={setDialog}>
|
||||
<DialogContent className="sm:max-w-lg">
|
||||
<DialogHeader>
|
||||
<DialogTitle>Nuevo Estudio Complementario</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>Tipo de Estudio</Label>
|
||||
<Input
|
||||
value={tipo}
|
||||
onChange={e => setTipo(e.target.value)}
|
||||
placeholder="Ej: Radiografía de tórax, ECG, Tomografía, etc."
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<Label>Resultado</Label>
|
||||
<textarea
|
||||
className="w-full p-2 border rounded-md text-sm min-h-[100px]"
|
||||
value={resultado}
|
||||
onChange={e => setResultado(e.target.value)}
|
||||
placeholder="Ingrese el resultado del estudio..."
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex justify-end gap-2 mt-4">
|
||||
<Button variant="outline" onClick={() => setDialog(false)}>Cancelar</Button>
|
||||
<Button onClick={handleGuardar} disabled={!tipo || !resultado}>
|
||||
<Save className="h-4 w-4 mr-2" />
|
||||
Guardar
|
||||
</Button>
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
|
||||
{estudiosFiltrados.length === 0 ? (
|
||||
<div className="text-center py-8 text-gray-400">
|
||||
<ClipboardList className="h-12 w-12 mx-auto mb-2 opacity-50" />
|
||||
<p>No hay estudios complementarios</p>
|
||||
</div>
|
||||
) : (
|
||||
<div className="space-y-3">
|
||||
{estudiosFiltrados.sort((a, b) => new Date(b.fecha).getTime() - new Date(a.fecha).getTime()).map(e => (
|
||||
<Card key={e.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">{e.fecha}</span>
|
||||
</div>
|
||||
<h4 className="font-semibold text-gray-900 dark:text-white">{e.tipo}</h4>
|
||||
<p className="text-sm text-gray-600 dark:text-gray-300 mt-1 whitespace-pre-wrap">{e.resultado}</p>
|
||||
</div>
|
||||
<Button size="sm" variant="outline" className="text-red-600 self-start" onClick={() => del(e.id)}>
|
||||
<Trash2 className="h-4 w-4" />
|
||||
</Button>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user