Implementar lógica de cálculo automático del sector (En Área / Fuera de Área) para Camas según su numeración y almacenar en la BD

This commit is contained in:
2026-08-09 20:17:44 +00:00
parent dc108a9699
commit 4ae43e713a
12 changed files with 319 additions and 47 deletions
+109
View File
@@ -0,0 +1,109 @@
const fs = require('fs');
let code = fs.readFileSync('server/db.js', 'utf8');
// Add computeSector function
const computeSectorCode = `
function computeSector(numero) {
if (!numero) return 'En Área';
const parts = String(numero).trim().split('-');
if (parts.length === 0) return 'En Área';
const salaStr = parts[0].trim();
const salaNum = parseInt(salaStr, 10);
if (isNaN(salaNum)) return 'En Área';
if (salaNum >= 300 && salaNum < 400 && salaNum % 2 !== 0) {
return 'Fuera de Área';
}
if (salaNum >= 400 && salaNum < 500) {
return 'Fuera de Área';
}
return 'En Área';
}
`;
// Find where to insert computeSector
code = code.replace("export function createCama(cama) {", computeSectorCode + "\nexport function createCama(cama) {");
// Update createCama
code = code.replace(
/export function createCama\(cama\) \{\s*const gId = cama.grupoId \|\| cama.areaId \|\| null;\s*db.prepare\('INSERT INTO camas \(id, numero, grupoId, areaId, tipo, estado, pacienteId, internacionId\) VALUES \(\?, \?, \?, \?, \?, \?, \?, \?\)'\)\.run\(cama.id, cama.numero, gId, gId, cama.tipo \|\| 'Estándar', cama.estado \|\| 'Disponible', cama.pacienteId \|\| null, cama.internacionId \|\| null\);\s*\}/,
`export function createCama(cama) {
const gId = cama.grupoId || cama.areaId || null;
const sector = computeSector(cama.numero);
db.prepare('INSERT INTO camas (id, numero, grupoId, areaId, tipo, estado, pacienteId, internacionId, sector) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)').run(cama.id, cama.numero, gId, gId, cama.tipo || 'Estándar', cama.estado || 'Disponible', cama.pacienteId || null, cama.internacionId || null, sector);
}`
);
// Update updateCama
code = code.replace(
/export function updateCama\(id, updates\) \{[\s\S]*?if \(fields\.length > 0\) \{/,
`export function updateCama(id, updates) {
const fields = [];
const values = [];
if (updates.numero !== undefined) {
fields.push('numero = ?');
values.push(updates.numero);
fields.push('sector = ?');
values.push(computeSector(updates.numero));
}
if (updates.tipo !== undefined) {
fields.push('tipo = ?');
values.push(updates.tipo);
}
if (updates.grupoId !== undefined) {
fields.push('grupoId = ?');
values.push(updates.grupoId);
fields.push('areaId = ?');
values.push(updates.grupoId);
}
if (updates.estado !== undefined) {
fields.push('estado = ?');
values.push(updates.estado);
}
if (updates.pacienteId !== undefined) {
fields.push('pacienteId = ?');
values.push(updates.pacienteId);
}
if (updates.internacionId !== undefined) {
fields.push('internacionId = ?');
values.push(updates.internacionId);
}
if (fields.length > 0) {`
);
// Update table schema
code = code.replace(
/CREATE TABLE IF NOT EXISTS camas \(\s*id TEXT PRIMARY KEY,\s*numero TEXT NOT NULL,\s*grupoId TEXT,\s*areaId TEXT,\s*tipo TEXT DEFAULT 'Estándar',\s*estado TEXT DEFAULT 'Disponible',\s*pacienteId TEXT,\s*internacionId TEXT\s*\);/,
`CREATE TABLE IF NOT EXISTS camas (
id TEXT PRIMARY KEY,
numero TEXT NOT NULL,
grupoId TEXT,
areaId TEXT,
tipo TEXT DEFAULT 'Estándar',
estado TEXT DEFAULT 'Disponible',
pacienteId TEXT,
internacionId TEXT,
sector TEXT DEFAULT 'En Área'
);
try {
db.prepare("ALTER TABLE camas ADD COLUMN sector TEXT DEFAULT 'En Área'").run();
} catch(e) {}
try {
// Initialize existing beds with their sector
const rows = db.prepare('SELECT id, numero FROM camas WHERE sector IS NULL OR sector = "En Área"').all();
for (const row of rows) {
if (!row.numero) continue;
const salaStr = String(row.numero).trim().split('-')[0].trim();
const salaNum = parseInt(salaStr, 10);
let s = 'En Área';
if (!isNaN(salaNum)) {
if (salaNum >= 300 && salaNum < 400 && salaNum % 2 !== 0) s = 'Fuera de Área';
else if (salaNum >= 400 && salaNum < 500) s = 'Fuera de Área';
}
db.prepare('UPDATE camas SET sector = ? WHERE id = ?').run(s, row.id);
}
} catch(e) {}`
);
fs.writeFileSync('server/db.js', code);
+10
View File
@@ -0,0 +1,10 @@
const fs = require('fs');
let code = fs.readFileSync('server/db.js', 'utf8');
code = code.replace(
/const __dirname = dirname\(fileURLToPath\(import\.meta\.url\)\);/,
`const currentDir = typeof __dirname !== 'undefined' ? __dirname : dirname(fileURLToPath(import.meta.url));`
);
code = code.replace(/__dirname/g, "currentDir");
fs.writeFileSync('server/db.js', code);
+40
View File
@@ -0,0 +1,40 @@
const fs = require('fs');
let code = fs.readFileSync('server/index.js', 'utf8');
const computeSectorCode = `
function computeSector(numero) {
if (!numero) return 'En Área';
const parts = String(numero).trim().split('-');
if (parts.length === 0) return 'En Área';
const salaStr = parts[0].trim();
const salaNum = parseInt(salaStr, 10);
if (isNaN(salaNum)) return 'En Área';
if (salaNum >= 300 && salaNum < 400 && salaNum % 2 !== 0) return 'Fuera de Área';
if (salaNum >= 400 && salaNum < 500) return 'Fuera de Área';
return 'En Área';
}
`;
// Insert computeSector somewhere at the top
code = code.replace("const PORT = process.env.PORT || 3000;", computeSectorCode + "\nconst PORT = process.env.PORT || 3000;");
code = code.replace(
/const stmt = db\.prepare\('INSERT OR IGNORE INTO camas \(id, numero, grupoId, areaId, tipo, estado\) VALUES \(\?, \?, \?, \?, \?, \?\)'\);\s*for \(const c of state\.camas\) \{\s*const gId = c\.grupoId \|\| c\.areaId \|\| null;\s*stmt\.run\(c\.id, c\.numero, gId, gId, c\.tipo, c\.estado\);\s*\}/,
`const stmt = db.prepare('INSERT OR IGNORE INTO camas (id, numero, grupoId, areaId, tipo, estado, sector) VALUES (?, ?, ?, ?, ?, ?, ?)');
for (const c of state.camas) {
const gId = c.grupoId || c.areaId || null;
const sector = computeSector(c.numero);
stmt.run(c.id, c.numero, gId, gId, c.tipo, c.estado, sector);
}`
);
code = code.replace(
/const stmt = db\.prepare\('INSERT INTO camas \(id, numero, areaId, tipo, estado\) VALUES \(\?, \?, \?, \?, \?\)'\);\s*for \(const c of updates\.camas\) \{\s*stmt\.run\(c\.id, c\.numero, c\.areaId, c\.tipo, c\.estado\);\s*\}/,
`const stmt = db.prepare('INSERT INTO camas (id, numero, grupoId, areaId, tipo, estado, sector) VALUES (?, ?, ?, ?, ?, ?, ?)');
for (const c of updates.camas) {
const sector = computeSector(c.numero);
stmt.run(c.id, c.numero, c.grupoId || c.areaId || null, c.grupoId || c.areaId || null, c.tipo, c.estado, sector);
}`
);
fs.writeFileSync('server/index.js', code);
+30
View File
@@ -0,0 +1,30 @@
const fs = require('fs');
let code = fs.readFileSync('src/hooks/useHospitalStore.ts', 'utf8');
code = code.replace(
/const nuevaCama: Cama = \{\s*\.\.\.cama,\s*id: generateUUID\(\),\s*\} as Cama;/,
`const nuevaCama: Cama = {
...cama,
id: generateUUID(),
sector: computeSector(cama.numero),
} as Cama;`
);
// We need to import computeSector if it's not imported.
// It is in utils.ts, and we already import isCamaFueraDeGrupo.
code = code.replace(
/import \{ getNombreProfesional, isCamaFueraDeGrupo \} from '@\/lib\/utils';/,
`import { getNombreProfesional, isCamaFueraDeGrupo, computeSector } from '@/lib/utils';`
);
code = code.replace(
/const actualizarCama = useCallback\(async \(id: string, datos: Partial<Cama>\) => \{\s*try \{\s*await apiCall\('PUT', `\/camas\/\$\{id\}`/,
`const actualizarCama = useCallback(async (id: string, datos: Partial<Cama>) => {
try {
if (datos.numero !== undefined) {
datos.sector = computeSector(datos.numero);
}
await apiCall('PUT', \`/camas/\${id}\``
);
fs.writeFileSync('src/hooks/useHospitalStore.ts', code);
+13
View File
@@ -0,0 +1,13 @@
const fs = require('fs');
let code = fs.readFileSync('src/hooks/useHospitalStore.ts', 'utf8');
code = code.replace(
/try \{\s*await apiCall\('PUT', `\/camas\/\$\{id\}`/,
`try {
if (datos.numero !== undefined) {
datos.sector = computeSector(datos.numero);
}
await apiCall('PUT', \`/camas/\${id}\``
);
fs.writeFileSync('src/hooks/useHospitalStore.ts', code);
+16
View File
@@ -0,0 +1,16 @@
const fs = require('fs');
let code = fs.readFileSync('src/lib/utils.ts', 'utf8');
code = code.replace(
/export function isCamaFueraDeGrupo\([\s\S]*?return false;\n\}/,
`export function isCamaFueraDeGrupo(
cama: { numero: string; grupoId?: string; areaId?: string; sector?: string },
gruposOrAreas?: { id: string; nombre: string }[]
): boolean {
if (cama.sector === 'Fuera de Área') return true;
if (cama.sector === 'En Área') return false;
return computeSector(cama.numero) === 'Fuera de Área';
}`
);
fs.writeFileSync('src/lib/utils.ts', code);
+61 -6
View File
@@ -4,12 +4,12 @@ import { fileURLToPath } from 'url';
import bcrypt from 'bcryptjs'; import bcrypt from 'bcryptjs';
import { existsSync, mkdirSync } from 'fs'; import { existsSync, mkdirSync } from 'fs';
const __dirname = dirname(fileURLToPath(import.meta.url)); const currentDir = typeof currentDir !== 'undefined' ? currentDir : dirname(fileURLToPath(import.meta.url));
const DB_PATH = __dirname + '/data/hospital.db'; const DB_PATH = currentDir + '/data/hospital.db';
// Ensure data directory exists // Ensure data directory exists
if (!existsSync(__dirname + '/data')) { if (!existsSync(currentDir + '/data')) {
mkdirSync(__dirname + '/data', { recursive: true }); mkdirSync(currentDir + '/data', { recursive: true });
} }
// Create persistent database connection with WAL mode and proper settings // Create persistent database connection with WAL mode and proper settings
@@ -80,8 +80,27 @@ export function initDb() {
tipo TEXT DEFAULT 'Estándar', tipo TEXT DEFAULT 'Estándar',
estado TEXT DEFAULT 'Disponible', estado TEXT DEFAULT 'Disponible',
pacienteId TEXT, pacienteId TEXT,
internacionId TEXT internacionId TEXT,
sector TEXT DEFAULT 'En Área'
); );
try {
db.prepare("ALTER TABLE camas ADD COLUMN sector TEXT DEFAULT 'En Área'").run();
} catch(e) {}
try {
// Initialize existing beds with their sector
const rows = db.prepare('SELECT id, numero FROM camas WHERE sector IS NULL OR sector = "En Área"').all();
for (const row of rows) {
if (!row.numero) continue;
const salaStr = String(row.numero).trim().split('-')[0].trim();
const salaNum = parseInt(salaStr, 10);
let s = 'En Área';
if (!isNaN(salaNum)) {
if (salaNum >= 300 && salaNum < 400 && salaNum % 2 !== 0) s = 'Fuera de Área';
else if (salaNum >= 400 && salaNum < 500) s = 'Fuera de Área';
}
db.prepare('UPDATE camas SET sector = ? WHERE id = ?').run(s, row.id);
}
} catch(e) {}
CREATE TABLE IF NOT EXISTS internaciones ( CREATE TABLE IF NOT EXISTS internaciones (
id TEXT PRIMARY KEY, id TEXT PRIMARY KEY,
@@ -408,6 +427,23 @@ export function getAllCamas() {
export function updateCama(id, updates) { export function updateCama(id, updates) {
const fields = []; const fields = [];
const values = []; const values = [];
if (updates.numero !== undefined) {
fields.push('numero = ?');
values.push(updates.numero);
fields.push('sector = ?');
values.push(computeSector(updates.numero));
}
if (updates.tipo !== undefined) {
fields.push('tipo = ?');
values.push(updates.tipo);
}
if (updates.grupoId !== undefined) {
fields.push('grupoId = ?');
values.push(updates.grupoId);
fields.push('areaId = ?');
values.push(updates.grupoId);
}
if (updates.estado !== undefined) { if (updates.estado !== undefined) {
fields.push('estado = ?'); fields.push('estado = ?');
values.push(updates.estado); values.push(updates.estado);
@@ -420,6 +456,7 @@ export function updateCama(id, updates) {
fields.push('internacionId = ?'); fields.push('internacionId = ?');
values.push(updates.internacionId); values.push(updates.internacionId);
} }
if (fields.length > 0) { if (fields.length > 0) {
values.push(id); values.push(id);
db.prepare(`UPDATE camas SET ${fields.join(', ')} WHERE id = ?`).run(values); db.prepare(`UPDATE camas SET ${fields.join(', ')} WHERE id = ?`).run(values);
@@ -544,9 +581,27 @@ export function deleteArea(id) {
} }
// Camas // Camas
function computeSector(numero) {
if (!numero) return 'En Área';
const parts = String(numero).trim().split('-');
if (parts.length === 0) return 'En Área';
const salaStr = parts[0].trim();
const salaNum = parseInt(salaStr, 10);
if (isNaN(salaNum)) return 'En Área';
if (salaNum >= 300 && salaNum < 400 && salaNum % 2 !== 0) {
return 'Fuera de Área';
}
if (salaNum >= 400 && salaNum < 500) {
return 'Fuera de Área';
}
return 'En Área';
}
export function createCama(cama) { export function createCama(cama) {
const gId = cama.grupoId || cama.areaId || null; const gId = cama.grupoId || cama.areaId || null;
db.prepare('INSERT INTO camas (id, numero, grupoId, areaId, tipo, estado, pacienteId, internacionId) VALUES (?, ?, ?, ?, ?, ?, ?, ?)').run(cama.id, cama.numero, gId, gId, cama.tipo || 'Estándar', cama.estado || 'Disponible', cama.pacienteId || null, cama.internacionId || null); const sector = computeSector(cama.numero);
db.prepare('INSERT INTO camas (id, numero, grupoId, areaId, tipo, estado, pacienteId, internacionId, sector) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)').run(cama.id, cama.numero, gId, gId, cama.tipo || 'Estándar', cama.estado || 'Disponible', cama.pacienteId || null, cama.internacionId || null, sector);
} }
export function deleteCama(id) { export function deleteCama(id) {
+6 -4
View File
@@ -95,10 +95,11 @@ app.put('/api/state', (req, res) => {
} }
if (state.camas?.length) { if (state.camas?.length) {
const stmt = db.prepare('INSERT OR IGNORE INTO camas (id, numero, grupoId, areaId, tipo, estado) VALUES (?, ?, ?, ?, ?, ?)'); const stmt = db.prepare('INSERT OR IGNORE INTO camas (id, numero, grupoId, areaId, tipo, estado, sector) VALUES (?, ?, ?, ?, ?, ?, ?)');
for (const c of state.camas) { for (const c of state.camas) {
const gId = c.grupoId || c.areaId || null; const gId = c.grupoId || c.areaId || null;
stmt.run(c.id, c.numero, gId, gId, c.tipo, c.estado); const sector = computeSector(c.numero);
stmt.run(c.id, c.numero, gId, gId, c.tipo, c.estado, sector);
} }
} }
@@ -224,9 +225,10 @@ app.put('/api/state/partial', (req, res) => {
if (updates.camas) { if (updates.camas) {
db.prepare('DELETE FROM camas').run(); db.prepare('DELETE FROM camas').run();
if (updates.camas.length > 0) { if (updates.camas.length > 0) {
const stmt = db.prepare('INSERT INTO camas (id, numero, areaId, tipo, estado) VALUES (?, ?, ?, ?, ?)'); const stmt = db.prepare('INSERT INTO camas (id, numero, grupoId, areaId, tipo, estado, sector) VALUES (?, ?, ?, ?, ?, ?, ?)');
for (const c of updates.camas) { for (const c of updates.camas) {
stmt.run(c.id, c.numero, c.areaId, c.tipo, c.estado); const sector = computeSector(c.numero);
stmt.run(c.id, c.numero, c.grupoId || c.areaId || null, c.grupoId || c.areaId || null, c.tipo, c.estado, sector);
} }
} }
} }
+5 -1
View File
@@ -1,6 +1,6 @@
import { useState, useEffect, useCallback } from 'react'; import { useState, useEffect, useCallback } from 'react';
import { toast } from 'sonner'; import { toast } from 'sonner';
import { getNombreProfesional, isCamaFueraDeGrupo } from '@/lib/utils'; import { getNombreProfesional, isCamaFueraDeGrupo, computeSector } from '@/lib/utils';
import type { import type {
Paciente, Paciente,
Cama, Cama,
@@ -291,6 +291,9 @@ export function useHospitalStore() {
return; return;
} }
try { try {
if (datos.numero !== undefined) {
datos.sector = computeSector(datos.numero);
}
await apiCall('PUT', `/camas/${id}`, datos); await apiCall('PUT', `/camas/${id}`, datos);
setState(prev => ({ setState(prev => ({
...prev, ...prev,
@@ -306,6 +309,7 @@ const agregarCama = useCallback(async (cama: Omit<Cama, 'id'>) => {
const nuevaCama: Cama = { const nuevaCama: Cama = {
...cama, ...cama,
id: generateUUID(), id: generateUUID(),
sector: computeSector(cama.numero),
} as Cama; } as Cama;
try { try {
await apiCall('POST', '/camas', nuevaCama); await apiCall('POST', '/camas', nuevaCama);
+14 -27
View File
@@ -38,42 +38,29 @@ export function calcularEdad(fechaNacimiento?: string): string | number {
return edad; return edad;
} }
export function isCamaFueraDeGrupo( export function isCamaFueraDeGrupo(
cama: { numero: string; grupoId?: string; areaId?: string }, cama: { numero: string; grupoId?: string; areaId?: string; sector?: string },
gruposOrAreas?: { id: string; nombre: string }[] _gruposOrAreas?: { id: string; nombre: string }[]
): boolean { ): boolean {
if (cama.grupoId === 'Fuera de Area' || cama.areaId === 'Fuera de Area') { if (cama.sector === 'Fuera de Área') return true;
return true; if (cama.sector === 'En Área') return false;
return computeSector(cama.numero) === 'Fuera de Área';
} }
if (gruposOrAreas) { export function computeSector(numero: string): "En Área" | "Fuera de Área" {
const targetId = cama.grupoId || cama.areaId; if (!numero) return 'En Área';
if (targetId) { const parts = String(numero).trim().split('-');
const g = gruposOrAreas.find(a => (a.nombre || '').toLowerCase().trim() === 'fuera de area'); if (parts.length === 0) return 'En Área';
if (g && targetId === g.id) {
return true;
}
}
}
const numero = cama.numero;
if (!numero) return false;
const parts = numero.trim().split('-');
if (parts.length === 0) return false;
const salaStr = parts[0].trim(); const salaStr = parts[0].trim();
const salaNum = parseInt(salaStr, 10); const salaNum = parseInt(salaStr, 10);
if (isNaN(salaNum)) return false; if (isNaN(salaNum)) return 'En Área';
// Formato 3XX que sean impares (301, 303, 305, etc.)
if (salaNum >= 300 && salaNum < 400 && salaNum % 2 !== 0) { if (salaNum >= 300 && salaNum < 400 && salaNum % 2 !== 0) {
return true; return 'Fuera de Área';
} }
// Formato 4XX (todas las 4XX, pares o impares)
if (salaNum >= 400 && salaNum < 500) { if (salaNum >= 400 && salaNum < 500) {
return true; return 'Fuera de Área';
} }
return 'En Área';
return false;
} }
+1 -1
View File
@@ -284,7 +284,7 @@ export function MapaCamas({
{getEstadoIcono(cama)} {getEstadoIcono(cama)}
</div> </div>
<p className="font-bold text-sm sm:text-lg">{cama.numero}</p> <p className="font-bold text-sm sm:text-lg">{cama.numero}</p>
<p className="text-xs opacity-75 truncate">{cama.grupoId ? grupoById[cama.grupoId] ?? 'Grupo desconocido' : 'Sin grupo'}</p> <p className="text-xs opacity-75 truncate">{cama.grupoId ? grupoById[cama.grupoId] ?? 'Grupo desconocido' : 'Sin grupo'} - {cama.sector || 'En Área'}</p>
<Badge variant="outline" className="mt-1 sm:mt-2 text-xs bg-white/50 dark:bg-gray-800 dark:text-gray-300"> <Badge variant="outline" className="mt-1 sm:mt-2 text-xs bg-white/50 dark:bg-gray-800 dark:text-gray-300">
{cama.tipo} {cama.tipo}
</Badge> </Badge>
+6
View File
@@ -37,6 +37,7 @@ export interface Cama {
estado: 'Disponible' | 'Ocupada' | 'Reparacion' | 'Reservada'; estado: 'Disponible' | 'Ocupada' | 'Reparacion' | 'Reservada';
pacienteId?: string; pacienteId?: string;
internacionId?: string; internacionId?: string;
sector?: "En Área" | "Fuera de Área";
} }
export interface Internacion { export interface Internacion {
@@ -100,6 +101,7 @@ export interface Laboratorio {
id: string; id: string;
pacienteId: string; pacienteId: string;
internacionId?: string; internacionId?: string;
sector?: "En Área" | "Fuera de Área";
fecha: string; fecha: string;
hora: string; hora: string;
tipo?: 'Hemograma' | 'Química sanguínea' | 'Urianálisis' | 'Coagulación' | 'Microbiología' | 'Inmunología' | 'Otros'; tipo?: 'Hemograma' | 'Química sanguínea' | 'Urianálisis' | 'Coagulación' | 'Microbiología' | 'Inmunología' | 'Otros';
@@ -120,6 +122,7 @@ export interface Glucemia {
id: string; id: string;
pacienteId: string; pacienteId: string;
internacionId?: string; internacionId?: string;
sector?: "En Área" | "Fuera de Área";
fecha: string; fecha: string;
hora: string; hora: string;
valor: number; // in mg% valor: number; // in mg%
@@ -130,6 +133,7 @@ export interface AcidoBase {
id: string; id: string;
pacienteId: string; pacienteId: string;
internacionId?: string; internacionId?: string;
sector?: "En Área" | "Fuera de Área";
fecha: string; fecha: string;
hora: string; hora: string;
ph: number; ph: number;
@@ -147,6 +151,7 @@ export interface Cultivo {
id: string; id: string;
pacienteId: string; pacienteId: string;
internacionId?: string; internacionId?: string;
sector?: "En Área" | "Fuera de Área";
fechaToma: string; fechaToma: string;
protocolo?: string; protocolo?: string;
fechaResultado?: string; fechaResultado?: string;
@@ -162,6 +167,7 @@ export interface EstudioComplementario {
id: string; id: string;
pacienteId: string; pacienteId: string;
internacionId?: string; internacionId?: string;
sector?: "En Área" | "Fuera de Área";
fecha: string; fecha: string;
tipo: string; tipo: string;
resultado: string; resultado: string;