feat: update hospital management system - cama fuera de área features and dashboard fixes

This commit is contained in:
2026-08-10 02:14:30 +00:00
parent 735ccb9920
commit 091bbc0e95
4 changed files with 65 additions and 31 deletions
+9 -10
View File
@@ -1269,28 +1269,27 @@ const actualizarInternacion = useCallback(async (internacionId: string, datos: P
}, [state.cultivos]);
const getEstadisticas = useCallback(() => {
const fueraDeGrupoList = state.camas.filter(c => isCamaFueraDeGrupo(c, state.grupos));
const camasActivas = state.camas.filter(c => !isCamaFueraDeGrupo(c, state.grupos));
const camaPrincipal = state.camas.filter(c => !isCamaFueraDeGrupo(c, state.grupos));
const camaOcupadas = state.camas.filter(c => c.estado === 'Ocupada' && !isCamaFueraDeGrupo(c, state.grupos)).length;
const camasDisponibles = state.camas.filter(c => c.estado === 'Disponible' && !isCamaFueraDeGrupo(c, state.grupos)).length;
const camasEnArea = state.camas.filter(c => !isCamaFueraDeGrupo(c));
const camasFueraDeArea = state.camas.filter(c => isCamaFueraDeGrupo(c));
const camasOcupadas = camasEnArea.filter(c => c.estado === 'Ocupada').length;
const camasDisponibles = camasEnArea.filter(c => c.estado === 'Disponible').length;
const camasMantenimiento = state.camas.filter(c => c.estado === 'Reparacion').length;
const internacionesActivas = state.internaciones.filter(i => i.activa).length;
const totalPacientes = state.pacientes.length;
const cultivosPendientes = state.cultivos.filter(c => c.estado === 'NAF/Pendiente').length;
return {
camaOcupadas,
camasOcupadas: camaOcupadas,
camasOcupadas,
camasDisponibles,
camasMantenimiento,
totalCamas: state.camas.length,
totalCamasActivas: camasActivas.length,
camasFueraDeGrupo: fueraDeGrupoList.length,
totalCamasActivas: camasEnArea.length,
camasFueraDeArea: camasFueraDeArea.length,
internacionesActivas,
totalPacientes,
cultivosPendientes,
porcentajeOcupacion: camaPrincipal.length > 0 ? Math.round((camaOcupadas / camaPrincipal.length) * 100) : 0,
porcentajeOcupacion: camasEnArea.length > 0 ? Math.round((camasOcupadas / camasEnArea.length) * 100) : 0,
};
}, [state]);
+6 -2
View File
@@ -55,11 +55,15 @@ export function computeSector(numero: string): "En Área" | "Fuera de Área" {
const salaNum = parseInt(salaStr, 10);
if (isNaN(salaNum)) return 'En Área';
if (salaNum >= 300 && salaNum < 400 && salaNum % 2 !== 0) {
// 3XX-YY where XX is odd (so the whole 3XX is odd)
if (salaStr.length === 3 && salaStr.startsWith('3') && salaNum % 2 !== 0) {
return 'Fuera de Área';
}
if (salaNum >= 400 && salaNum < 500) {
// All those starting with 4
if (salaStr.startsWith('4')) {
return 'Fuera de Área';
}
return 'En Área';
}
+3 -3
View File
@@ -75,7 +75,7 @@ export function Dashboard({
<CardContent>
<div className="flex items-baseline gap-2">
<span className="text-3xl font-bold">{estadisticas.porcentajeOcupacion}%</span>
<span className="text-sm text-gray-500 dark:text-gray-400">{estadisticas.camasOcupadas}/{estadisticas.totalCamas}</span>
<span className="text-sm text-gray-500 dark:text-gray-400">{estadisticas.camasOcupadas}/{estadisticas.totalCamasActivas}</span>
</div>
<p className="text-xs text-gray-400 dark:text-gray-400 mt-1">
{estadisticas.camasDisponibles} camas disponibles
@@ -87,7 +87,7 @@ export function Dashboard({
<CardHeader className="pb-2">
<CardTitle className="text-sm font-medium text-gray-500 dark:text-gray-400 flex items-center gap-2">
<Users className="h-4 w-4" />
Camas Fuera de Área
Camas fuera de área
</CardTitle>
</CardHeader>
<CardContent>
@@ -95,7 +95,7 @@ export function Dashboard({
<span className="text-3xl font-bold">{estadisticas.camasFueraDeArea}</span>
</div>
<p className="text-xs text-gray-400 dark:text-gray-400 mt-1">
Camas Fuera de Grupo
Total de camas fuera de área
</p>
</CardContent>
</Card>
+47 -16
View File
@@ -40,6 +40,7 @@ export function NuevoIngreso({ pacientes, camas, grupos, onIniciarInternacion, o
const [antecedentesEnfermedadActual, setAntecedentesEnfermedadActual] = useState('');
const [apache, setApache] = useState('');
const [derivacion, setDerivacion] = useState('');
const [grupoSeleccionado, setGrupoSeleccionado] = useState<string>('');
const [isSubmitting, setIsSubmitting] = useState(false);
const resetFormulario = () => {
@@ -47,6 +48,7 @@ export function NuevoIngreso({ pacientes, camas, grupos, onIniciarInternacion, o
setBusqueda('');
setCamaSeleccionada('');
setCamaInput('');
setGrupoSeleccionado('');
setFechaIngresoHospital('');
setFechaIngresoClinica('');
setMotivoConsulta('');
@@ -57,11 +59,6 @@ export function NuevoIngreso({ pacientes, camas, grupos, onIniciarInternacion, o
setDerivacion('');
};
const normalizeStr = (str?: string) => (str || '').normalize('NFD').replace(/[\u0300-\u036f]/g, '').trim().toLowerCase();
const grupoFueraDeGrupo = grupos.find(a => normalizeStr(a.nombre) === 'fuera de grupo');
const grupoFueraDeGrupoId = grupoFueraDeGrupo?.id || grupos[0]?.id || 'fuera-de-grupo';
const pacientesSinInternar = pacientes.filter(p => {
const internacionActiva = internaciones.find(i => i.pacienteId === p.id && i.activa);
return !internacionActiva;
@@ -127,6 +124,11 @@ export function NuevoIngreso({ pacientes, camas, grupos, onIniciarInternacion, o
return;
}
if (!grupoSeleccionado) {
toast.error('Debe seleccionar un grupo para el seguimiento');
return;
}
if (!diagnosticoIngreso.trim()) {
toast.error('Debe completar el Diagnóstico de Ingreso');
return;
@@ -146,22 +148,22 @@ export function NuevoIngreso({ pacientes, camas, grupos, onIniciarInternacion, o
try {
let camaId = '';
let newGrupoId = '';
if (modoCama === 'escribir') {
const numeroCama = camaInput.trim();
const camaExistente = camas.find(c => c.numero === numeroCama);
if (camaExistente) {
camaId = camaExistente.id;
newGrupoId = camaExistente.grupoId || grupoFueraDeGrupoId;
toast.error(`La cama ${numeroCama} ya existe en el sistema. Seleccione la cama del listado o ingrese un número diferente.`);
setIsSubmitting(false);
return;
} else if (onAgregarCama) {
camaId = await onAgregarCama({
numero: numeroCama,
grupoId: grupoFueraDeGrupoId,
grupoId: grupoSeleccionado,
tipo: 'General',
estado: 'Ocupada'
estado: 'Ocupada',
sector: 'Fuera de Área'
});
newGrupoId = grupoFueraDeGrupoId;
} else {
toast.error('No se puede crear la cama');
setIsSubmitting(false);
@@ -175,13 +177,12 @@ export function NuevoIngreso({ pacientes, camas, grupos, onIniciarInternacion, o
return;
}
camaId = camaSeleccionada;
newGrupoId = camaElegida.grupoId || grupoFueraDeGrupoId;
}
await onIniciarInternacion({
pacienteId: pacienteSeleccionado,
camaId: camaId,
grupoId: newGrupoId,
grupoId: grupoSeleccionado,
medicoIngresante: effectiveMedico.trim(),
diagnosticoIngreso: diagnosticoIngreso.trim(),
motivoConsulta: motivoConsulta.trim() || undefined,
@@ -317,17 +318,47 @@ export function NuevoIngreso({ pacientes, camas, grupos, onIniciarInternacion, o
<Button
className="flex-1 text-xs sm:text-sm"
variant={modoCama === 'escribir' ? 'default' : 'outline'}
onClick={() => setModoCama('escribir')}
onClick={() => {
setModoCama('escribir');
setGrupoSeleccionado('');
}}
>
Escribir Cama
Cama Fuera Área
</Button>
</div>
{modoCama === 'escribir' && (
<div className="space-y-2">
<Label>Grupo de Seguimiento *</Label>
<Select value={grupoSeleccionado} onValueChange={setGrupoSeleccionado}>
<SelectTrigger>
<SelectValue placeholder="Seleccionar grupo" />
</SelectTrigger>
<SelectContent>
{grupos.map(g => (
<SelectItem key={g.id} value={g.id}>
{g.nombre}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
)}
{modoCama === 'seleccionar' ? (
<>
<div>
<Label>Cama *</Label>
<Select value={camaSeleccionada} onValueChange={setCamaSeleccionada}>
<Select
value={camaSeleccionada}
onValueChange={(val) => {
setCamaSeleccionada(val);
const cama = camas.find(c => c.id === val);
if (cama?.grupoId) {
setGrupoSeleccionado(cama.grupoId);
}
}}
>
<SelectTrigger>
<SelectValue placeholder="Seleccionar cama" />
</SelectTrigger>