Mejorar tema oscuro, añadir dropdownmenu en laboratorios, unificar estilos de botones a outline

This commit is contained in:
2026-04-15 18:35:40 -03:00
parent 83e4ad1c36
commit 4573e6e3d8
9 changed files with 207 additions and 123 deletions
+8 -4
View File
@@ -13,6 +13,7 @@ import { EditIngreso } from '@/sections/EditIngreso';
import { Spinner } from '@/components/ui/spinner'; import { Spinner } from '@/components/ui/spinner';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import { ThemeProvider } from '@/contexts/ThemeContext'; import { ThemeProvider } from '@/contexts/ThemeContext';
import { Toaster } from '@/components/ui/sonner';
function AppContent() { function AppContent() {
const store = useHospitalStore(); const store = useHospitalStore();
@@ -127,7 +128,7 @@ function AppContent() {
if (!internacion || !paciente) { if (!internacion || !paciente) {
return ( return (
<div className="p-4 text-center"> <div className="p-4 text-center">
<p className="text-gray-500">Internación no encontrada</p> <p className="text-gray-500 dark:text-gray-400">Internación no encontrada</p>
<Button onClick={() => store.setVista('internaciones')}>Volver a Internaciones</Button> <Button onClick={() => store.setVista('internaciones')}>Volver a Internaciones</Button>
</div> </div>
); );
@@ -160,7 +161,7 @@ function AppContent() {
/> />
); );
} }
case 'nuevoingreso': case 'nuevoingreso':
return ( return (
<NuevoIngreso <NuevoIngreso
pacientes={store.pacientes} pacientes={store.pacientes}
@@ -178,7 +179,7 @@ case 'nuevoingreso':
if (!internacion || !paciente) { if (!internacion || !paciente) {
return ( return (
<div className="p-4 text-center"> <div className="p-4 text-center">
<p className="text-gray-500">Internación no encontrada</p> <p className="text-gray-500 dark:text-gray-400">Internación no encontrada</p>
<Button onClick={() => store.setVista('internaciones')}>Volver a Internaciones</Button> <Button onClick={() => store.setVista('internaciones')}>Volver a Internaciones</Button>
</div> </div>
); );
@@ -197,6 +198,7 @@ case 'nuevoingreso':
/> />
); );
} }
default:
return null; return null;
} }
}; };
@@ -210,8 +212,10 @@ case 'nuevoingreso':
function App() { function App() {
return ( return (
<ThemeProvider> // @ts-expect-error - next-themes types mismatch
<ThemeProvider attribute="class" defaultTheme="system" enableSystem>
<AppContent /> <AppContent />
<Toaster position="top-right" />
</ThemeProvider> </ThemeProvider>
); );
} }
+10 -31
View File
@@ -1,40 +1,19 @@
import { createContext, useContext, useEffect, useState, type ReactNode } from 'react'; import { ThemeProvider as NextThemesProvider, useTheme as useNextTheme } from 'next-themes';
import type { ReactNode } from 'react';
interface ThemeContextType {
isDarkMode: boolean;
toggleDarkMode: () => void;
}
const ThemeContext = createContext<ThemeContextType | undefined>(undefined);
export function ThemeProvider({ children }: { children: ReactNode }) { export function ThemeProvider({ children }: { children: ReactNode }) {
const [isDarkMode, setIsDarkMode] = useState(() => {
const saved = localStorage.getItem('darkMode');
return saved ? JSON.parse(saved) : false;
});
useEffect(() => {
localStorage.setItem('darkMode', JSON.stringify(isDarkMode));
if (isDarkMode) {
document.documentElement.classList.add('dark');
} else {
document.documentElement.classList.remove('dark');
}
}, [isDarkMode]);
const toggleDarkMode = () => setIsDarkMode((prev: boolean) => !prev);
return ( return (
<ThemeContext.Provider value={{ isDarkMode, toggleDarkMode }}> <NextThemesProvider attribute="class" defaultTheme="system" enableSystem>
{children} {children}
</ThemeContext.Provider> </NextThemesProvider>
); );
} }
export function useTheme() { export function useTheme() {
const context = useContext(ThemeContext); const { theme, setTheme } = useNextTheme();
if (!context) { const isDarkMode = theme === 'dark';
throw new Error('useTheme must be used within ThemeProvider'); const toggleDarkMode = () => setTheme(theme === 'dark' ? 'light' : 'dark');
} return { isDarkMode, toggleDarkMode, theme, setTheme };
return context;
} }
export { NextThemesProvider };
+9 -9
View File
@@ -184,7 +184,7 @@ export function Cultivos({
</div> </div>
<Dialog open={dialogoNuevoAbierto} onOpenChange={setDialogoNuevoAbierto}> <Dialog open={dialogoNuevoAbierto} onOpenChange={setDialogoNuevoAbierto}>
<DialogTrigger asChild> <DialogTrigger asChild>
<Button onClick={resetFormularioNuevo} className="w-full sm:w-auto"> <Button variant="outline" onClick={resetFormularioNuevo} className="w-full sm:w-auto">
<Plus className="h-4 w-4 mr-2" /> <Plus className="h-4 w-4 mr-2" />
Nuevo Cultivo Nuevo Cultivo
</Button> </Button>
@@ -230,7 +230,7 @@ export function Cultivos({
return p ? `${p.apellido}, ${p.nombre} - DNI: ${p.dni}` : ''; return p ? `${p.apellido}, ${p.nombre} - DNI: ${p.dni}` : '';
})()} })()}
</Badge> </Badge>
<Button size="sm" variant="ghost" onClick={() => setPacienteSeleccionado('')}>Cambiar</Button> <Button size="sm" variant="outline" onClick={() => setPacienteSeleccionado('')}>Cambiar</Button>
</div> </div>
)} )}
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4"> <div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
@@ -269,7 +269,7 @@ export function Cultivos({
</div> </div>
<div className="flex justify-end gap-2"> <div className="flex justify-end gap-2">
<Button variant="outline" onClick={() => setDialogoNuevoAbierto(false)}><X className="h-4 w-4 mr-2" />Cancelar</Button> <Button variant="outline" onClick={() => setDialogoNuevoAbierto(false)}><X className="h-4 w-4 mr-2" />Cancelar</Button>
<Button onClick={handleAgregar} disabled={!pacienteSeleccionado || !fechaToma || !tipoMuestra}> <Button variant="outline" onClick={handleAgregar} disabled={!pacienteSeleccionado || !fechaToma || !tipoMuestra}>
<Plus className="h-4 w-4 mr-2" /> <Plus className="h-4 w-4 mr-2" />
Guardar Guardar
</Button> </Button>
@@ -310,7 +310,7 @@ export function Cultivos({
<X className="h-4 w-4 mr-2" /> <X className="h-4 w-4 mr-2" />
Cancelar Cancelar
</Button> </Button>
<Button onClick={handleParcial} disabled={!germen}> <Button variant="outline" onClick={handleParcial} disabled={!germen}>
<Save className="h-4 w-4 mr-2" /> <Save className="h-4 w-4 mr-2" />
Guardar Parcial Guardar Parcial
</Button> </Button>
@@ -368,7 +368,7 @@ export function Cultivos({
<X className="h-4 w-4 mr-2" /> <X className="h-4 w-4 mr-2" />
Cancelar Cancelar
</Button> </Button>
<Button onClick={handleDefinitivo} disabled={estadoResultado === 'Positivo' && !germen}> <Button variant="outline" onClick={handleDefinitivo} disabled={estadoResultado === 'Positivo' && !germen}>
<Save className="h-4 w-4 mr-2" /> <Save className="h-4 w-4 mr-2" />
Guardar Definitivo Guardar Definitivo
</Button> </Button>
@@ -452,11 +452,11 @@ export function Cultivos({
<div className="flex flex-wrap items-center gap-1 sm:gap-2 ml-auto sm:ml-0"> <div className="flex flex-wrap items-center gap-1 sm:gap-2 ml-auto sm:ml-0">
{onActualizarCultivo && cultivo.estado === 'NAF/Pendiente' && ( {onActualizarCultivo && cultivo.estado === 'NAF/Pendiente' && (
<> <>
<Button size="sm" className="text-xs px-2 py-1" onClick={() => abrirParcial(cultivo)}> <Button size="sm" variant="outline" className="text-xs px-2 py-1" onClick={() => abrirParcial(cultivo)}>
<AlertCircle className="h-3 w-3 sm:h-4 sm:w-4 sm:mr-1" /> <AlertCircle className="h-3 w-3 sm:h-4 sm:w-4 sm:mr-1" />
<span className="hidden sm:inline">Parcial</span> <span className="hidden sm:inline">Parcial</span>
</Button> </Button>
<Button size="sm" variant="default" className="text-xs px-2 py-1" onClick={() => abrirDefinitivo(cultivo)}> <Button size="sm" variant="outline" className="text-xs px-2 py-1" onClick={() => abrirDefinitivo(cultivo)}>
<CheckCircle2 className="h-3 w-3 sm:h-4 sm:w-4 sm:mr-1" /> <CheckCircle2 className="h-3 w-3 sm:h-4 sm:w-4 sm:mr-1" />
<span className="hidden sm:inline">Definitivo</span> <span className="hidden sm:inline">Definitivo</span>
</Button> </Button>
@@ -468,7 +468,7 @@ export function Cultivos({
<Pencil className="h-3 w-3 sm:h-4 sm:w-4 sm:mr-1" /> <Pencil className="h-3 w-3 sm:h-4 sm:w-4 sm:mr-1" />
<span className="hidden sm:inline">Editar</span> <span className="hidden sm:inline">Editar</span>
</Button> </Button>
<Button size="sm" variant="default" className="text-xs px-2 py-1" onClick={() => abrirDefinitivo(cultivo)}> <Button size="sm" variant="outline" className="text-xs px-2 py-1" onClick={() => abrirDefinitivo(cultivo)}>
<CheckCircle2 className="h-3 w-3 sm:h-4 sm:w-4 sm:mr-1" /> <CheckCircle2 className="h-3 w-3 sm:h-4 sm:w-4 sm:mr-1" />
<span className="hidden sm:inline">Definitivo</span> <span className="hidden sm:inline">Definitivo</span>
</Button> </Button>
@@ -479,7 +479,7 @@ export function Cultivos({
<Pencil className="h-3 w-3 sm:h-4 sm:w-4" /> <Pencil className="h-3 w-3 sm:h-4 sm:w-4" />
</Button> </Button>
)} )}
<Button size="sm" variant="ghost" className="text-red-600 text-xs px-2 py-1" onClick={() => onEliminarCultivo(cultivo.id)}> <Button size="sm" variant="outline" className="text-red-600 text-xs px-2 py-1" onClick={() => onEliminarCultivo(cultivo.id)}>
<Trash2 className="h-3 w-3 sm:h-4 sm:w-4" /> <Trash2 className="h-3 w-3 sm:h-4 sm:w-4" />
</Button> </Button>
</div> </div>
+4 -4
View File
@@ -82,7 +82,7 @@ export function EditIngreso({
return ( return (
<div className="space-y-6 dark:text-white"> <div className="space-y-6 dark:text-white">
<div className="flex items-center justify-between"> <div className="flex flex-col sm:flex-row items-start sm:items-center justify-between gap-4">
<div className="flex items-center gap-4"> <div className="flex items-center gap-4">
<Button variant="ghost" size="icon" onClick={onVolver}> <Button variant="ghost" size="icon" onClick={onVolver}>
<ArrowLeft className="h-5 w-5" /> <ArrowLeft className="h-5 w-5" />
@@ -95,7 +95,7 @@ export function EditIngreso({
<p className="text-gray-500 dark:text-gray-400">Modificar datos de internación</p> <p className="text-gray-500 dark:text-gray-400">Modificar datos de internación</p>
</div> </div>
</div> </div>
<div className="flex gap-2"> <div className="flex gap-2 ml-auto sm:ml-0">
<Button variant="outline" onClick={() => onVolver()}> <Button variant="outline" onClick={() => onVolver()}>
<X className="h-4 w-4 mr-2" /> <X className="h-4 w-4 mr-2" />
Cancelar Cancelar
@@ -120,13 +120,13 @@ export function EditIngreso({
{selectedPaciente ? ( {selectedPaciente ? (
<div className="space-y-2"> <div className="space-y-2">
<div className="flex flex-wrap gap-2"> <div className="flex flex-wrap gap-2">
<Badge variant="outline" className="bg-primary/10 text-primary border-primary/30"> <Badge variant="outline" className="bg-green-50 text-green-700 dark:bg-green-950 dark:text-green-300">
{selectedPaciente?.apellido}, {selectedPaciente?.nombre} {selectedPaciente?.apellido}, {selectedPaciente?.nombre}
</Badge> </Badge>
<Badge variant="outline" className="bg-primary/10 text-primary border-primary/30"> <Badge variant="outline" className="bg-primary/10 text-primary border-primary/30">
{selectedPaciente?.fechaNacimiento ? calcularEdad(selectedPaciente.fechaNacimiento) + ' años' : ''} {selectedPaciente?.fechaNacimiento ? calcularEdad(selectedPaciente.fechaNacimiento) + ' años' : ''}
</Badge> </Badge>
<Badge variant="outline" className="bg-primary/10 text-primary border-primary/30"> <Badge variant="outline" className="bg-blue-50 text-blue-700 border-blue-200">
DNI: {selectedPaciente?.dni} DNI: {selectedPaciente?.dni}
</Badge> </Badge>
</div> </div>
+14 -15
View File
@@ -192,7 +192,7 @@ export function Evoluciones({
</div> </div>
<Dialog open={dialogoAbierto} onOpenChange={setDialogoAbierto}> <Dialog open={dialogoAbierto} onOpenChange={setDialogoAbierto}>
<DialogTrigger asChild> <DialogTrigger asChild>
<Button onClick={resetFormulario}> <Button variant="outline" onClick={resetFormulario}>
<Plus className="h-4 w-4 mr-2" /> <Plus className="h-4 w-4 mr-2" />
Nueva Evolución Nueva Evolución
</Button> </Button>
@@ -390,13 +390,13 @@ export function Evoluciones({
<X className="h-4 w-4 mr-2" /> <X className="h-4 w-4 mr-2" />
Cancelar Cancelar
</Button> </Button>
<Button <Button variant="outline"
onClick={handleGuardar} onClick={handleGuardar}
disabled={!medico || (!evolucionEditando && !pacienteSeleccionado)} disabled={!medico || (!evolucionEditando && !pacienteSeleccionado)}
> >
<Save className="h-4 w-4 mr-2" /> <Save className="h-4 w-4 mr-2" />
Guardar Evolución Guardar Evolución
</Button> </Button>
</div> </div>
</DialogContent> </DialogContent>
</Dialog> </Dialog>
@@ -462,13 +462,12 @@ export function Evoluciones({
<Pencil className="h-4 w-4" /> <Pencil className="h-4 w-4" />
</Button> </Button>
)} )}
<Button <Button variant="outline"
variant="ghost" size="sm"
size="sm" className="text-red-600"
className="text-red-600 hover:bg-red-50" onClick={() => onEliminarEvolucion(evolucion.id)}
onClick={() => onEliminarEvolucion(evolucion.id)} >
> <Trash2 className="h-4 w-4" />
<Trash2 className="h-4 w-4" />
</Button> </Button>
</div> </div>
</div> </div>
+126 -24
View File
@@ -22,7 +22,11 @@ import {
Wind, Wind,
Droplets, Droplets,
Save, Save,
X X,
MoreHorizontal,
Edit,
ClipboardList,
TrendingUp
} from 'lucide-react'; } from 'lucide-react';
import { Card, CardContent } from '@/components/ui/card'; import { Card, CardContent } from '@/components/ui/card';
import { Badge } from '@/components/ui/badge'; 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 { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table'; 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 } from '@/types';
import { formatDateDDMMYYYY } from '@/lib/utils'; import { formatDateDDMMYYYY } from '@/lib/utils';
@@ -212,7 +218,7 @@ export function HistoriaClinica({
<div className="flex items-center gap-4 mb-4"> <div className="flex items-center gap-4 mb-4">
<div> <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"> <p className="text-gray-500">
Cama: {cama?.numero || 'N/A'} | {paciente.apellido}, {paciente.nombre} | {paciente.fechaNacimiento ? calcularEdad(paciente.fechaNacimiento) + ' años' : ''} | DNI: {paciente.dni} Cama: {cama?.numero || 'N/A'} | {paciente.apellido}, {paciente.nombre} | {paciente.fechaNacimiento ? calcularEdad(paciente.fechaNacimiento) + ' años' : ''} | DNI: {paciente.dni}
</p> </p>
@@ -534,6 +540,8 @@ function SeccionLaboratorios({ lab, patientId, add, update, del }: {
}) { }) {
const [dialog, setDialog] = useState(false); const [dialog, setDialog] = useState(false);
const [obsDialog, setObsDialog] = 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 [edit, setEdit] = useState<Laboratorio | null>(null);
const [obsLab, setObsLab] = useState<Laboratorio | null>(null); const [obsLab, setObsLab] = useState<Laboratorio | null>(null);
const [fecha, setFecha] = useState(new Date().toISOString().split('T')[0]); 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 [tp, setTp] = useState('');
const [kptt, setKptt] = useState(''); const [kptt, setKptt] = useState('');
const [rin, setRin] = 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']) => { const getEstadoColor = (estado: ResultadoLaboratorio['estado']) => {
switch (estado) { switch (estado) {
@@ -657,8 +682,11 @@ function SeccionLaboratorios({ lab, patientId, add, update, del }: {
return ( return (
<div className="space-y-4"> <div className="space-y-4">
<div className="flex justify-end"> <div className="flex justify-between">
<Button onClick={() => { reset(); setDialog(true); }}><Plus className="h-4 w-4 mr-2" />Nuevo Laboratorio</Button> <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> </div>
<Dialog open={dialog} onOpenChange={setDialog}> <Dialog open={dialog} onOpenChange={setDialog}>
<DialogContent className="sm:max-w-2xl max-h-[90vh] overflow-y-auto"> <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"> <div className="space-y-2">
<p className="text-sm text-gray-500">{obsLab?.fecha}</p> <p className="text-sm text-gray-500">{obsLab?.fecha}</p>
<p className="text-sm text-gray-500">{obsLab?.tipo}</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> <p className="whitespace-pre-wrap">{obsLab?.observaciones}</p>
</div> </div>
</div> </div>
@@ -733,6 +761,64 @@ function SeccionLaboratorios({ lab, patientId, add, update, del }: {
</DialogContent> </DialogContent>
</Dialog> </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> : {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"> <div className="overflow-x-auto">
<Table> <Table>
@@ -756,7 +842,7 @@ function SeccionLaboratorios({ lab, patientId, add, update, del }: {
<TableHead>TP</TableHead> <TableHead>TP</TableHead>
<TableHead>KPTT</TableHead> <TableHead>KPTT</TableHead>
<TableHead>RIN</TableHead> <TableHead>RIN</TableHead>
<TableHead></TableHead> <TableHead>Acciones</TableHead>
</TableRow> </TableRow>
</TableHeader> </TableHeader>
<TableBody> <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, 'KPTT')}>{getValor(l, 'KPTT')}</TableCell>
<TableCell className={getValorStyle(l, 'INR')}>{getValor(l, 'INR')}</TableCell> <TableCell className={getValorStyle(l, 'INR')}>{getValor(l, 'INR')}</TableCell>
<TableCell> <TableCell>
<div className="flex items-center gap-1"> <DropdownMenu>
{l.observaciones && <Button size="sm" variant="ghost" onClick={() => { setObsLab(l); setObsDialog(true); }}><ChevronRight className="h-4 w-4" /></Button>} <DropdownMenuTrigger asChild>
<Button size="sm" variant="ghost" onClick={() => loadEdit(l)}><Pencil className="h-4 w-4" /></Button> <Button size="sm" variant="ghost" className="h-8 w-8 p-0">
<Button size="sm" variant="ghost" className="text-red-600" onClick={() => del(l.id)}><Trash2 className="h-4 w-4" /></Button> <MoreHorizontal className="h-4 w-4" />
</div> </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> </TableCell>
</TableRow> </TableRow>
))} ))}
@@ -893,8 +995,8 @@ function SeccionEvoluciones({ evos, internacionId, add, update, del }: {
return ( return (
<div className="space-y-4"> <div className="space-y-4">
<div className="flex justify-end"> <div className="flex">
<Button onClick={() => { reset(); setDialog(true); }}><Plus className="h-4 w-4 mr-2" />Nueva Evolución</Button> <Button variant="outline" onClick={() => { reset(); setDialog(true); }}><Plus className="h-4 w-4 mr-2" />Nueva Evolución</Button>
</div> </div>
<Dialog open={dialog} onOpenChange={setDialog}> <Dialog open={dialog} onOpenChange={setDialog}>
<DialogContent className="sm:max-w-3xl max-h-[90vh] overflow-y-auto"> <DialogContent className="sm:max-w-3xl max-h-[90vh] overflow-y-auto">
@@ -1037,8 +1139,8 @@ function SeccionAcidosBase({ ab, patientId, add, del }: {
return ( return (
<div className="space-y-4"> <div className="space-y-4">
<div className="flex justify-end"> <div className="flex">
<Button onClick={() => { reset(); setDialog(true); }}><Plus className="h-4 w-4 mr-2" />Nueva Gasometría</Button> <Button variant="outline" onClick={() => { reset(); setDialog(true); }}><Plus className="h-4 w-4 mr-2" />Nueva Gasometría</Button>
</div> </div>
<Dialog open={dialog} onOpenChange={setDialog}> <Dialog open={dialog} onOpenChange={setDialog}>
<DialogContent className="sm:max-w-2xl max-h-[90vh] overflow-y-auto"> <DialogContent className="sm:max-w-2xl max-h-[90vh] overflow-y-auto">
@@ -1212,8 +1314,8 @@ const resetRes = () => {
return ( return (
<div className="space-y-4"> <div className="space-y-4">
<div className="flex justify-end"> <div className="flex">
<Button onClick={() => { reset(); setDialog(true); }}><Plus className="h-4 w-4 mr-2" />Nuevo Cultivo</Button> <Button variant="outline" onClick={() => { reset(); setDialog(true); }}><Plus className="h-4 w-4 mr-2" />Nuevo Cultivo</Button>
</div> </div>
<Dialog open={dialog} onOpenChange={setDialog}> <Dialog open={dialog} onOpenChange={setDialog}>
<DialogContent className="sm:max-w-lg"> <DialogContent className="sm:max-w-lg">
@@ -1242,7 +1344,7 @@ const resetRes = () => {
</div> </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><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>
<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> </DialogContent>
</Dialog> </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>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><Label>Resistente a:</Label><Input value={resistente} onChange={e => setResistente(e.target.value)} placeholder="Ej: Oxacilina" /></div>
</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> </DialogContent>
</Dialog> </Dialog>
@@ -1288,7 +1390,7 @@ const resetRes = () => {
</> </>
)} )}
</div> </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> </DialogContent>
</Dialog> </Dialog>
@@ -1312,18 +1414,18 @@ const resetRes = () => {
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
{c.estado === 'NAF/Pendiente' && ( {c.estado === 'NAF/Pendiente' && (
<> <>
<Button size="sm" onClick={() => openParcial(c)}><AlertCircle className="h-4 w-4 mr-1" />Parcial</Button> <Button size="sm" variant="outline" 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={() => { openDefinitivo(c); setIsParcialMode(false); }}><CheckCircle2 className="h-4 w-4 mr-1" />Definitivo</Button>
</> </>
)} )}
{c.estado === 'Parcial' && ( {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="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>} {(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>
</div> </div>
{c.protocolo && <p className="text-xs text-gray-500">Protocolo: {c.protocolo}</p>} {c.protocolo && <p className="text-xs text-gray-500">Protocolo: {c.protocolo}</p>}
+13 -13
View File
@@ -145,7 +145,7 @@ export function Internaciones({
</div> </div>
<Dialog open={dialogoNuevaAbierto} onOpenChange={setDialogoNuevaAbierto}> <Dialog open={dialogoNuevaAbierto} onOpenChange={setDialogoNuevaAbierto}>
<DialogTrigger asChild> <DialogTrigger asChild>
<Button onClick={() => onNuevoIngreso ? onNuevoIngreso() : setDialogoNuevaAbierto(true)}> <Button variant="outline" onClick={() => onNuevoIngreso ? onNuevoIngreso() : setDialogoNuevaAbierto(true)}>
<Plus className="h-4 w-4 mr-2" /> <Plus className="h-4 w-4 mr-2" />
Nuevo Ingreso Nuevo Ingreso
</Button> </Button>
@@ -202,7 +202,7 @@ export function Internaciones({
return p ? `${p.apellido}, ${p.nombre} — DNI: ${p.dni}` : ''; return p ? `${p.apellido}, ${p.nombre} — DNI: ${p.dni}` : '';
})()} })()}
</Badge> </Badge>
<Button size="sm" variant="ghost" onClick={() => setPacienteSeleccionado('')}>Cambiar</Button> <Button size="sm" variant="outline" onClick={() => setPacienteSeleccionado('')}>Cambiar</Button>
</div> </div>
)} )}
</div> </div>
@@ -257,12 +257,12 @@ export function Internaciones({
placeholder="Nombre del médico ingresante" placeholder="Nombre del médico ingresante"
/> />
</div> </div>
<Button <Button variant="outline"
className="w-full" className="w-full"
onClick={handleIniciarInternacion} onClick={handleIniciarInternacion}
disabled={!pacienteSeleccionado || !camaSeleccionada || !motivoConsulta || !enfermedadActual || !medicoIngresante} disabled={!pacienteSeleccionado || !camaSeleccionada || !motivoConsulta || !enfermedadActual || !medicoIngresante}
> >
<Plus className="h-4 w-4 mr-2" /> <Plus className="h-4 w-4 mr-2" />
Iniciar Internación Iniciar Internación
</Button> </Button>
</div> </div>
@@ -424,20 +424,20 @@ export function Internaciones({
placeholder="Ingrese el diagnóstico de egreso..." placeholder="Ingrese el diagnóstico de egreso..."
/> />
</div> </div>
<Button <Button variant="outline"
className="w-full" className="w-full"
onClick={handleFinalizarInternacion} onClick={handleFinalizarInternacion}
disabled={!fechaEgreso || !diagnosticoEgreso || !motivoEgreso} disabled={!fechaEgreso || !diagnosticoEgreso || !motivoEgreso}
> >
<CheckCircle2 className="h-4 w-4 mr-2" /> <CheckCircle2 className="h-4 w-4 mr-2" />
Confirmar Egreso Confirmar Egreso
</Button> </Button>
</div> </div>
</DialogContent> </DialogContent>
</Dialog> </Dialog>
)} )}
<Button onClick={() => { if (typeof onVerHC === 'function') onVerHC(internacion.id); }}>Ver HC</Button> <Button variant="outline" onClick={() => { if (typeof onVerHC === 'function') onVerHC(internacion.id); }}>Ver HC</Button>
</div> </div>
</div> </div>
</CardContent> </CardContent>
+12 -12
View File
@@ -188,14 +188,14 @@ return (
{camas.filter(c => c.estado === 'Ocupada').length} Ocupadas {camas.filter(c => c.estado === 'Ocupada').length} Ocupadas
</Badge> </Badge>
<div className="ml-4 flex items-center gap-2 flex-wrap sm:flex-nowrap"> <div className="ml-4 flex items-center gap-2 flex-wrap sm:flex-nowrap">
<Button onClick={() => { <Button variant="outline" onClick={() => {
setEditingAreaId(null); setEditingAreaId(null);
setAreaNombre(''); setAreaNombre('');
setAreaDialogOpen(true); setAreaDialogOpen(true);
}} className="text-xs px-2 py-1 sm:text-sm sm:px-4 sm:py-2"> }} className="text-xs px-2 py-1 sm:text-sm sm:px-4 sm:py-2">
Administrar Áreas Administrar Áreas
</Button> </Button>
<Button onClick={() => { <Button variant="outline" onClick={() => {
setEditingBed(null); setEditingBed(null);
setBedNumero(''); setBedNumero('');
setBedTipo('General'); setBedTipo('General');
@@ -327,7 +327,7 @@ return (
{cama.estado === 'Disponible' && ( {cama.estado === 'Disponible' && (
<Dialog open={dialogoAbierto} onOpenChange={setDialogoAbierto}> <Dialog open={dialogoAbierto} onOpenChange={setDialogoAbierto}>
<DialogTrigger asChild> <DialogTrigger asChild>
<Button <Button variant="outline"
className="w-full" className="w-full"
onClick={() => { onClick={() => {
setCamaSeleccionada(cama); setCamaSeleccionada(cama);
@@ -398,14 +398,14 @@ return (
placeholder="Nombre del médico..." placeholder="Nombre del médico..."
/> />
</div> </div>
<Button <Button variant="outline"
className="w-full" className="w-full"
onClick={handleOcuparCama} onClick={handleOcuparCama}
disabled={!pacienteSeleccionado || !diagnostico || !enfermedadActual || !medico} disabled={!pacienteSeleccionado || !diagnostico || !enfermedadActual || !medico}
> >
<Plus className="h-4 w-4 mr-1" /> <Plus className="h-4 w-4 mr-1" />
Iniciar Internación Iniciar Internación
</Button> </Button>
</div> </div>
</DialogContent> </DialogContent>
</Dialog> </Dialog>
@@ -491,7 +491,7 @@ return (
<Button size="sm" variant="outline" onClick={() => setAreaDialogOpen(false)}> <Button size="sm" variant="outline" onClick={() => setAreaDialogOpen(false)}>
<X className="h-4 w-4" /> <X className="h-4 w-4" />
</Button> </Button>
<Button size="sm" onClick={() => { <Button size="sm" variant="outline" onClick={() => {
if (!areaNombre.trim()) return alert('Nombre requerido'); if (!areaNombre.trim()) return alert('Nombre requerido');
if (editingAreaId) onActualizarArea(editingAreaId, { nombre: areaNombre }); if (editingAreaId) onActualizarArea(editingAreaId, { nombre: areaNombre });
else onAgregarArea({ nombre: areaNombre }); else onAgregarArea({ nombre: areaNombre });
@@ -507,10 +507,10 @@ return (
<div key={a.id} className="flex items-center justify-between"> <div key={a.id} className="flex items-center justify-between">
<div>{a.nombre}</div> <div>{a.nombre}</div>
<div className="flex gap-1"> <div className="flex gap-1">
<Button size="sm" variant="ghost" onClick={() => { setEditingAreaId(a.id); setAreaNombre(a.nombre); }}> <Button size="sm" variant="outline" onClick={() => { setEditingAreaId(a.id); setAreaNombre(a.nombre); }}>
<Edit2 className="h-4 w-4" /> <Edit2 className="h-4 w-4" />
</Button> </Button>
<Button size="sm" variant="ghost" className="text-red-600" onClick={() => { if (confirm('Eliminar área?')) onEliminarArea(a.id); }}> <Button size="sm" variant="outline" className="text-red-600" onClick={() => { if (confirm('Eliminar área?')) onEliminarArea(a.id); }}>
<Trash className="h-4 w-4" /> <Trash className="h-4 w-4" />
</Button> </Button>
</div> </div>
@@ -568,8 +568,8 @@ return (
Eliminar Eliminar
</Button> </Button>
)} )}
<Button onClick={() => setBedDialogOpen(false)}><X className="h-4 w-4 mr-2" />Cancelar</Button> <Button variant="outline" onClick={() => setBedDialogOpen(false)}><X className="h-4 w-4 mr-2" />Cancelar</Button>
<Button onClick={() => { <Button variant="outline" onClick={() => {
if (!bedNumero.trim()) return alert('Número requerido'); if (!bedNumero.trim()) return alert('Número requerido');
if (editingBed) { if (editingBed) {
onActualizarCama(editingBed.id, { numero: bedNumero, tipo: bedTipo, areaId: bedAreaId }); onActualizarCama(editingBed.id, { numero: bedNumero, tipo: bedTipo, areaId: bedAreaId });
+9 -9
View File
@@ -147,7 +147,7 @@ export function Pacientes({
</div> </div>
<Dialog open={dialogoAbierto} onOpenChange={setDialogoAbierto}> <Dialog open={dialogoAbierto} onOpenChange={setDialogoAbierto}>
<DialogTrigger asChild> <DialogTrigger asChild>
<Button onClick={resetFormulario}> <Button variant="outline" onClick={resetFormulario}>
<Plus className="h-4 w-4 mr-2" /> <Plus className="h-4 w-4 mr-2" />
Nuevo Paciente Nuevo Paciente
</Button> </Button>
@@ -299,13 +299,13 @@ export function Pacientes({
<X className="h-4 w-4 mr-2" /> <X className="h-4 w-4 mr-2" />
Cancelar Cancelar
</Button> </Button>
<Button <Button variant="outline"
onClick={handleGuardar} onClick={handleGuardar}
disabled={!nombre || !apellido || !dni || !fechaNacimiento} disabled={!nombre || !apellido || !dni || !fechaNacimiento}
> >
<Save className="h-4 w-4 mr-2" /> <Save className="h-4 w-4 mr-2" />
{pacienteEditando ? 'Guardar Cambios' : 'Crear Paciente'} {pacienteEditando ? 'Guardar Cambios' : 'Crear Paciente'}
</Button> </Button>
</div> </div>
</DialogContent> </DialogContent>
</Dialog> </Dialog>
@@ -441,7 +441,7 @@ export function Pacientes({
{busqueda ? 'No se encontraron pacientes con esa búsqueda' : 'No hay pacientes registrados'} {busqueda ? 'No se encontraron pacientes con esa búsqueda' : 'No hay pacientes registrados'}
</p> </p>
{!busqueda && ( {!busqueda && (
<Button className="mt-4" onClick={() => setDialogoAbierto(true)}> <Button variant="outline" className="mt-4" onClick={() => setDialogoAbierto(true)}>
<Plus className="h-4 w-4 mr-2" /> <Plus className="h-4 w-4 mr-2" />
Agregar primer paciente Agregar primer paciente
</Button> </Button>