Mejorar tema oscuro, añadir dropdownmenu en laboratorios, unificar estilos de botones a outline
This commit is contained in:
@@ -22,7 +22,11 @@ import {
|
||||
Wind,
|
||||
Droplets,
|
||||
Save,
|
||||
X
|
||||
X,
|
||||
MoreHorizontal,
|
||||
Edit,
|
||||
ClipboardList,
|
||||
TrendingUp
|
||||
} from 'lucide-react';
|
||||
import { Card, CardContent } from '@/components/ui/card';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
@@ -33,6 +37,8 @@ import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
|
||||
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 { formatDateDDMMYYYY } from '@/lib/utils';
|
||||
|
||||
@@ -212,7 +218,7 @@ export function HistoriaClinica({
|
||||
<div className="flex items-center gap-4 mb-4">
|
||||
|
||||
<div>
|
||||
<h1 className="text-2xl font-bold text-gray-900">Historia Clínica</h1>
|
||||
<h1 className="text-2xl font-bold text-gray-900 dark:text-white flex items-center gap-2"><ClipboardList className="h-6 w-6" />Historia Clínica</h1>
|
||||
<p className="text-gray-500">
|
||||
Cama: {cama?.numero || 'N/A'} | {paciente.apellido}, {paciente.nombre} | {paciente.fechaNacimiento ? calcularEdad(paciente.fechaNacimiento) + ' años' : ''} | DNI: {paciente.dni}
|
||||
</p>
|
||||
@@ -534,6 +540,8 @@ function SeccionLaboratorios({ lab, patientId, add, update, del }: {
|
||||
}) {
|
||||
const [dialog, setDialog] = useState(false);
|
||||
const [obsDialog, setObsDialog] = useState(false);
|
||||
const [evolDialog, setEvolDialog] = useState(false);
|
||||
const [selectedLab, setSelectedLab] = useState<Laboratorio | null>(null);
|
||||
const [edit, setEdit] = useState<Laboratorio | null>(null);
|
||||
const [obsLab, setObsLab] = useState<Laboratorio | null>(null);
|
||||
const [fecha, setFecha] = useState(new Date().toISOString().split('T')[0]);
|
||||
@@ -556,6 +564,23 @@ function SeccionLaboratorios({ lab, patientId, add, update, del }: {
|
||||
const [tp, setTp] = useState('');
|
||||
const [kptt, setKptt] = useState('');
|
||||
const [rin, setRin] = useState('');
|
||||
const [parametroEvolucion, setParametroEvolucion] = useState('');
|
||||
|
||||
const parametrosLaboratorio = [
|
||||
'Hematocrito', 'Hemoglobina', 'Leucocitos', 'Plaquetas',
|
||||
'Glucemia', 'Urea', 'Creatinina',
|
||||
'Sodio', 'Potasio', 'Cloro',
|
||||
'Bilirrubina Total', 'Bilirrubina Directa', 'GOT', 'GPT',
|
||||
'Tiempo de Protrombina', 'KPTT', 'INR'
|
||||
];
|
||||
|
||||
const datosEvolucion = lab
|
||||
.filter(l => l.resultados.some(r => r.parametro === parametroEvolucion))
|
||||
.sort((a, b) => new Date(a.fecha).getTime() - new Date(b.fecha).getTime())
|
||||
.map(l => {
|
||||
const r = l.resultados.find(r => r.parametro === parametroEvolucion);
|
||||
return { fecha: l.fecha, valor: r ? parseFloat(String(r.valor)) : null };
|
||||
});
|
||||
|
||||
const getEstadoColor = (estado: ResultadoLaboratorio['estado']) => {
|
||||
switch (estado) {
|
||||
@@ -657,8 +682,11 @@ function SeccionLaboratorios({ lab, patientId, add, update, del }: {
|
||||
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<div className="flex justify-end">
|
||||
<Button onClick={() => { reset(); setDialog(true); }}><Plus className="h-4 w-4 mr-2" />Nuevo Laboratorio</Button>
|
||||
<div className="flex justify-between">
|
||||
<div className="flex gap-2">
|
||||
<Button variant="outline" onClick={() => { reset(); setDialog(true); }}><Plus className="h-4 w-4 mr-2" />Nuevo Laboratorio</Button>
|
||||
<Button variant="outline" onClick={() => { setSelectedLab(lab[0] || null); setParametroEvolucion('Hematocrito'); setEvolDialog(true); }}><TrendingUp className="h-4 w-4 mr-2" />Evolución</Button>
|
||||
</div>
|
||||
</div>
|
||||
<Dialog open={dialog} onOpenChange={setDialog}>
|
||||
<DialogContent className="sm:max-w-2xl max-h-[90vh] overflow-y-auto">
|
||||
@@ -725,7 +753,7 @@ function SeccionLaboratorios({ lab, patientId, add, update, del }: {
|
||||
<div className="space-y-2">
|
||||
<p className="text-sm text-gray-500">{obsLab?.fecha}</p>
|
||||
<p className="text-sm text-gray-500">{obsLab?.tipo}</p>
|
||||
<div className="bg-gray-50 p-3 rounded-lg border border-gray-200">
|
||||
<div className="bg-gray-50 p-3 rounded-lg border border-gray-200 dark:bg-gray-800 dark:border-gray-700">
|
||||
<p className="whitespace-pre-wrap">{obsLab?.observaciones}</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -733,6 +761,64 @@ function SeccionLaboratorios({ lab, patientId, add, update, del }: {
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
|
||||
<Dialog open={evolDialog} onOpenChange={setEvolDialog}>
|
||||
<DialogContent className="sm:max-w-2xl max-h-[90vh] overflow-y-auto">
|
||||
<DialogHeader>
|
||||
<DialogTitle className="flex items-center gap-2">
|
||||
<TrendingUp className="h-5 w-5" />
|
||||
Evolución de Laboratorio
|
||||
</DialogTitle>
|
||||
</DialogHeader>
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<Label>Parámetro a visualizar</Label>
|
||||
<Select value={parametroEvolucion} onValueChange={setParametroEvolucion}>
|
||||
<SelectTrigger><SelectValue placeholder="Seleccionar parámetro" /></SelectTrigger>
|
||||
<SelectContent>
|
||||
{parametrosLaboratorio.map(p => (
|
||||
<SelectItem key={p} value={p}>{p}</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
{parametroEvolucion && datosEvolucion.length > 0 && (
|
||||
<div className="h-64 bg-muted/30 rounded-lg p-4">
|
||||
<ResponsiveContainer width="100%" height="100%">
|
||||
<LineChart data={datosEvolucion}>
|
||||
<CartesianGrid strokeDasharray="3 3" className="opacity-30" />
|
||||
<XAxis dataKey="fecha" tick={{ fontSize: 12 }} />
|
||||
<YAxis tick={{ fontSize: 12 }} domain={['auto', 'auto']} />
|
||||
<Tooltip
|
||||
contentStyle={{
|
||||
backgroundColor: 'hsl(var(--card))',
|
||||
border: '1px solid hsl(var(--border))',
|
||||
borderRadius: '8px'
|
||||
}}
|
||||
/>
|
||||
<Line
|
||||
type="monotone"
|
||||
dataKey="valor"
|
||||
stroke="hsl(var(--primary))"
|
||||
strokeWidth={2}
|
||||
dot={{ fill: 'hsl(var(--primary))', r: 4 }}
|
||||
/>
|
||||
</LineChart>
|
||||
</ResponsiveContainer>
|
||||
</div>
|
||||
)}
|
||||
{parametroEvolucion && datosEvolucion.length === 0 && (
|
||||
<div className="text-center py-8 text-gray-400">
|
||||
<TrendingUp className="h-12 w-12 mx-auto mb-2 opacity-50" />
|
||||
<p>No hay datos para este parámetro</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex justify-end gap-2 mt-4">
|
||||
<Button onClick={() => setEvolDialog(false)}>Cerrar</Button>
|
||||
</div>
|
||||
</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">
|
||||
<Table>
|
||||
@@ -756,7 +842,7 @@ function SeccionLaboratorios({ lab, patientId, add, update, del }: {
|
||||
<TableHead>TP</TableHead>
|
||||
<TableHead>KPTT</TableHead>
|
||||
<TableHead>RIN</TableHead>
|
||||
<TableHead></TableHead>
|
||||
<TableHead>Acciones</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
@@ -781,11 +867,27 @@ function SeccionLaboratorios({ lab, patientId, add, update, del }: {
|
||||
<TableCell className={getValorStyle(l, 'KPTT')}>{getValor(l, 'KPTT')}</TableCell>
|
||||
<TableCell className={getValorStyle(l, 'INR')}>{getValor(l, 'INR')}</TableCell>
|
||||
<TableCell>
|
||||
<div className="flex items-center gap-1">
|
||||
{l.observaciones && <Button size="sm" variant="ghost" onClick={() => { setObsLab(l); setObsDialog(true); }}><ChevronRight className="h-4 w-4" /></Button>}
|
||||
<Button size="sm" variant="ghost" onClick={() => loadEdit(l)}><Pencil className="h-4 w-4" /></Button>
|
||||
<Button size="sm" variant="ghost" className="text-red-600" onClick={() => del(l.id)}><Trash2 className="h-4 w-4" /></Button>
|
||||
</div>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button size="sm" variant="ghost" className="h-8 w-8 p-0">
|
||||
<MoreHorizontal className="h-4 w-4" />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end">
|
||||
<DropdownMenuItem onClick={() => loadEdit(l)}>
|
||||
<Edit className="mr-2 h-4 w-4" />
|
||||
Editar
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem onClick={() => del(l.id)} className="text-red-600">
|
||||
<Trash2 className="mr-2 h-4 w-4" />
|
||||
Eliminar
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem onClick={() => { setObsLab(l); setObsDialog(true); }}>
|
||||
<ClipboardList className="mr-2 h-4 w-4" />
|
||||
Ver Observaciones
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
@@ -893,8 +995,8 @@ function SeccionEvoluciones({ evos, internacionId, add, update, del }: {
|
||||
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<div className="flex justify-end">
|
||||
<Button onClick={() => { reset(); setDialog(true); }}><Plus className="h-4 w-4 mr-2" />Nueva Evolución</Button>
|
||||
<div className="flex">
|
||||
<Button variant="outline" onClick={() => { reset(); setDialog(true); }}><Plus className="h-4 w-4 mr-2" />Nueva Evolución</Button>
|
||||
</div>
|
||||
<Dialog open={dialog} onOpenChange={setDialog}>
|
||||
<DialogContent className="sm:max-w-3xl max-h-[90vh] overflow-y-auto">
|
||||
@@ -1037,8 +1139,8 @@ function SeccionAcidosBase({ ab, patientId, add, del }: {
|
||||
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<div className="flex justify-end">
|
||||
<Button onClick={() => { reset(); setDialog(true); }}><Plus className="h-4 w-4 mr-2" />Nueva Gasometría</Button>
|
||||
<div className="flex">
|
||||
<Button variant="outline" onClick={() => { reset(); setDialog(true); }}><Plus className="h-4 w-4 mr-2" />Nueva Gasometría</Button>
|
||||
</div>
|
||||
<Dialog open={dialog} onOpenChange={setDialog}>
|
||||
<DialogContent className="sm:max-w-2xl max-h-[90vh] overflow-y-auto">
|
||||
@@ -1212,8 +1314,8 @@ const resetRes = () => {
|
||||
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<div className="flex justify-end">
|
||||
<Button onClick={() => { reset(); setDialog(true); }}><Plus className="h-4 w-4 mr-2" />Nuevo Cultivo</Button>
|
||||
<div className="flex">
|
||||
<Button variant="outline" onClick={() => { reset(); setDialog(true); }}><Plus className="h-4 w-4 mr-2" />Nuevo Cultivo</Button>
|
||||
</div>
|
||||
<Dialog open={dialog} onOpenChange={setDialog}>
|
||||
<DialogContent className="sm:max-w-lg">
|
||||
@@ -1242,7 +1344,7 @@ const resetRes = () => {
|
||||
</div>
|
||||
<div><Label>Observaciones</Label><textarea className="w-full p-2 border rounded-md text-sm min-h-[60px]" value={observaciones} onChange={e => setObservaciones(e.target.value)} /></div>
|
||||
</div>
|
||||
<div className="flex justify-end gap-2 mt-4"><Button variant="outline" onClick={() => setDialog(false)}><X className="h-4 w-4 mr-2" />Cancelar</Button><Button onClick={handle}><Plus className="h-4 w-4 mr-2" />Guardar</Button></div>
|
||||
<div className="flex justify-end gap-2 mt-4"><Button variant="outline" onClick={() => setDialog(false)}><X className="h-4 w-4 mr-2" />Cancelar</Button><Button variant="outline" onClick={handle}><Plus className="h-4 w-4 mr-2" />Guardar</Button></div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
|
||||
@@ -1259,7 +1361,7 @@ const resetRes = () => {
|
||||
<div><Label>Sensible a:</Label><Input value={sensible} onChange={e => setSensible(e.target.value)} placeholder="Ej: Amikacina, Vancomicina" /></div>
|
||||
<div><Label>Resistente a:</Label><Input value={resistente} onChange={e => setResistente(e.target.value)} placeholder="Ej: Oxacilina" /></div>
|
||||
</div>
|
||||
<div className="flex justify-end gap-2 mt-4"><Button variant="outline" onClick={() => { resetRes(); setResDialog(false); }}><X className="h-4 w-4 mr-2" />Cancelar</Button><Button onClick={handleParcial} disabled={!germen}><Save className="h-4 w-4 mr-2" />Guardar Parcial</Button></div>
|
||||
<div className="flex justify-end gap-2 mt-4"><Button variant="outline" onClick={() => { resetRes(); setResDialog(false); }}><X className="h-4 w-4 mr-2" />Cancelar</Button><Button variant="outline" onClick={handleParcial} disabled={!germen}><Save className="h-4 w-4 mr-2" />Guardar Parcial</Button></div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
|
||||
@@ -1288,7 +1390,7 @@ const resetRes = () => {
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex justify-end gap-2 mt-4"><Button variant="outline" onClick={() => { resetRes(); setEditDialog(false); }}><X className="h-4 w-4 mr-2" />Cancelar</Button><Button onClick={handleDefinitivo} disabled={estadoResultado === 'Positivo' && !germen}><Save className="h-4 w-4 mr-2" />Guardar Definitivo</Button></div>
|
||||
<div className="flex justify-end gap-2 mt-4"><Button variant="outline" onClick={() => { resetRes(); setEditDialog(false); }}><X className="h-4 w-4 mr-2" />Cancelar</Button><Button variant="outline" onClick={handleDefinitivo} disabled={estadoResultado === 'Positivo' && !germen}><Save className="h-4 w-4 mr-2" />Guardar Definitivo</Button></div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
|
||||
@@ -1312,18 +1414,18 @@ const resetRes = () => {
|
||||
<div className="flex items-center gap-2">
|
||||
{c.estado === 'NAF/Pendiente' && (
|
||||
<>
|
||||
<Button size="sm" onClick={() => openParcial(c)}><AlertCircle className="h-4 w-4 mr-1" />Parcial</Button>
|
||||
<Button size="sm" variant="default" onClick={() => { openDefinitivo(c); setIsParcialMode(false); }}><CheckCircle2 className="h-4 w-4 mr-1" />Definitivo</Button>
|
||||
<Button size="sm" variant="outline" onClick={() => openParcial(c)}><AlertCircle className="h-4 w-4 mr-1" />Parcial</Button>
|
||||
<Button size="sm" variant="outline" onClick={() => { openDefinitivo(c); setIsParcialMode(false); }}><CheckCircle2 className="h-4 w-4 mr-1" />Definitivo</Button>
|
||||
</>
|
||||
)}
|
||||
{c.estado === 'Parcial' && (
|
||||
<>
|
||||
<Button size="sm" variant="outline" onClick={() => openParcial(c)}><Pencil className="h-4 w-4 mr-1" />Editar Parcial</Button>
|
||||
<Button size="sm" variant="default" onClick={() => openDefinitivo(c)}><CheckCircle2 className="h-4 w-4 mr-1" />Definitivo</Button>
|
||||
<Button size="sm" variant="outline" onClick={() => openDefinitivo(c)}><CheckCircle2 className="h-4 w-4 mr-1" />Definitivo</Button>
|
||||
</>
|
||||
)}
|
||||
{(c.estado === 'Positivo' || c.estado === 'Negativo') && <Button size="sm" variant="outline" onClick={() => openDefinitivo(c)}><Pencil className="h-4 w-4" /></Button>}
|
||||
<Button size="sm" variant="ghost" className="text-red-600" onClick={() => del(c.id)}><Trash2 /></Button>
|
||||
<Button size="sm" variant="outline" className="text-red-600" onClick={() => del(c.id)}><Trash2 /></Button>
|
||||
</div>
|
||||
</div>
|
||||
{c.protocolo && <p className="text-xs text-gray-500">Protocolo: {c.protocolo}</p>}
|
||||
|
||||
Reference in New Issue
Block a user