Migrate to MariaDB backend and fix bed management

- Switch from SQLite to MariaDB for database backend
- Fix bed state updates on admission (iniciarInternacion)
- Fix bed updates on admission edit (actualizarInternacion)
- Fix bed updates on discharge (finalizarInternacion): delete if fuera de area, mark available otherwise
- Fix NuevoIngreso to wait for cama creation before creating internacion
- Fix patient undefined check in Cultivos
- Update CORS to allow all origins
- Remove unused files
This commit is contained in:
2026-04-24 10:50:05 -03:00
parent 175e2d285a
commit 705c200f1b
13 changed files with 694 additions and 2072 deletions
+54 -2
View File
@@ -47,6 +47,7 @@
"input-otp": "^1.4.2",
"jspdf": "^4.2.1",
"lucide-react": "^0.562.0",
"mariadb": "^3.5.2",
"next-themes": "^0.4.6",
"pdf-lib": "^1.17.1",
"react": "^19.2.0",
@@ -4815,6 +4816,12 @@
"dev": true,
"license": "MIT"
},
"node_modules/@types/geojson": {
"version": "7946.0.16",
"resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.16.tgz",
"integrity": "sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==",
"license": "MIT"
},
"node_modules/@types/json-schema": {
"version": "7.0.15",
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
@@ -4826,7 +4833,6 @@
"version": "24.10.4",
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.4.tgz",
"integrity": "sha512-vnDVpYPMzs4wunl27jHrfmwojOGKya0xyM3sH+UE5iv5uPS6vX7UIoh6m+vQc5LGBq52HBKPIn/zcSZVzeDEZg==",
"dev": true,
"license": "MIT",
"dependencies": {
"undici-types": "~7.16.0"
@@ -6321,6 +6327,15 @@
"license": "MIT",
"optional": true
},
"node_modules/denque": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz",
"integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==",
"license": "Apache-2.0",
"engines": {
"node": ">=0.10"
}
},
"node_modules/depd": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
@@ -7977,6 +7992,44 @@
"license": "ISC",
"optional": true
},
"node_modules/mariadb": {
"version": "3.5.2",
"resolved": "https://registry.npmjs.org/mariadb/-/mariadb-3.5.2.tgz",
"integrity": "sha512-9rztrI4nouxAY/82a+RlzzZ5ie2vxu2eYclkBvTy1ATXH1B9cnvZ0O71Pzsy/mlfDb5P3HhOg0JzQKkDRhctyA==",
"license": "LGPL-2.1-or-later",
"dependencies": {
"@types/geojson": "^7946.0.16",
"@types/node": ">=18",
"denque": "^2.1.0",
"iconv-lite": "^0.7.2",
"lru-cache": "^10.4.3"
},
"engines": {
"node": ">= 18"
}
},
"node_modules/mariadb/node_modules/iconv-lite": {
"version": "0.7.2",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz",
"integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==",
"license": "MIT",
"dependencies": {
"safer-buffer": ">= 2.1.2 < 3.0.0"
},
"engines": {
"node": ">=0.10.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/express"
}
},
"node_modules/mariadb/node_modules/lru-cache": {
"version": "10.4.3",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz",
"integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
"license": "ISC"
},
"node_modules/math-intrinsics": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
@@ -10340,7 +10393,6 @@
"version": "7.16.0",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz",
"integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==",
"dev": true,
"license": "MIT"
},
"node_modules/unicode-canonical-property-names-ecmascript": {
+1
View File
@@ -49,6 +49,7 @@
"input-otp": "^1.4.2",
"jspdf": "^4.2.1",
"lucide-react": "^0.562.0",
"mariadb": "^3.5.2",
"next-themes": "^0.4.6",
"pdf-lib": "^1.17.1",
"react": "^19.2.0",
+4 -1
View File
@@ -19,7 +19,10 @@ import { initDb,
} from './db-mariadb.js';
const app = express();
app.use(cors());
app.use(cors({
origin: true,
credentials: true
}));
app.use(express.json({ limit: '50mb' }));
// UUID generator
+2 -2
View File
@@ -1,5 +1,5 @@
import mariadb from 'mariadb';
import bcrypt from 'bcryptjs';
import * as mariadb from 'mariadb';
import * as bcrypt from 'bcryptjs';
// MariaDB connection pool
const pool = mariadb.createPool({
+57 -420
View File
@@ -8,12 +8,19 @@
"name": "hospital-server",
"version": "1.0.0",
"dependencies": {
"better-sqlite3": "^11.7.0",
"bcryptjs": "^2.4.3",
"cors": "^2.8.5",
"express": "^4.21.2",
"http-proxy-middleware": "^3.0.5"
"http-proxy-middleware": "^3.0.5",
"mariadb": "^3.4.0"
}
},
"node_modules/@types/geojson": {
"version": "7946.0.16",
"resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.16.tgz",
"integrity": "sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==",
"license": "MIT"
},
"node_modules/@types/http-proxy": {
"version": "1.17.17",
"resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.17.tgz",
@@ -51,57 +58,12 @@
"integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==",
"license": "MIT"
},
"node_modules/base64-js": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
"integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
],
"node_modules/bcryptjs": {
"version": "2.4.3",
"resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.4.3.tgz",
"integrity": "sha512-V/Hy/X9Vt7f3BbPJEi8BdVFMByHi+jNXrYkW3huaybV/kQ0KJg0Y6PkEMbn+zeT+i+SiKZ/HMqJGIIt4LZDqNQ==",
"license": "MIT"
},
"node_modules/better-sqlite3": {
"version": "11.10.0",
"resolved": "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-11.10.0.tgz",
"integrity": "sha512-EwhOpyXiOEL/lKzHz9AW1msWFNzGc/z+LzeB3/jnFJpxu+th2yqvzsSWas1v9jgs9+xiXJcD5A8CJxAG2TaghQ==",
"hasInstallScript": true,
"license": "MIT",
"dependencies": {
"bindings": "^1.5.0",
"prebuild-install": "^7.1.1"
}
},
"node_modules/bindings": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz",
"integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==",
"license": "MIT",
"dependencies": {
"file-uri-to-path": "1.0.0"
}
},
"node_modules/bl": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz",
"integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==",
"license": "MIT",
"dependencies": {
"buffer": "^5.5.0",
"inherits": "^2.0.4",
"readable-stream": "^3.4.0"
}
},
"node_modules/body-parser": {
"version": "1.20.4",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.4.tgz",
@@ -138,30 +100,6 @@
"node": ">=8"
}
},
"node_modules/buffer": {
"version": "5.7.1",
"resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
"integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
],
"license": "MIT",
"dependencies": {
"base64-js": "^1.3.1",
"ieee754": "^1.1.13"
}
},
"node_modules/bytes": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
@@ -200,12 +138,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/chownr": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",
"integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==",
"license": "ISC"
},
"node_modules/content-disposition": {
"version": "0.5.4",
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
@@ -268,28 +200,13 @@
"ms": "2.0.0"
}
},
"node_modules/decompress-response": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz",
"integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==",
"license": "MIT",
"dependencies": {
"mimic-response": "^3.1.0"
},
"node_modules/denque": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz",
"integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==",
"license": "Apache-2.0",
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/deep-extend": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz",
"integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==",
"license": "MIT",
"engines": {
"node": ">=4.0.0"
"node": ">=0.10"
}
},
"node_modules/depd": {
@@ -311,15 +228,6 @@
"npm": "1.2.8000 || >= 1.4.16"
}
},
"node_modules/detect-libc": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz",
"integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==",
"license": "Apache-2.0",
"engines": {
"node": ">=8"
}
},
"node_modules/dunder-proto": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
@@ -349,15 +257,6 @@
"node": ">= 0.8"
}
},
"node_modules/end-of-stream": {
"version": "1.4.5",
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz",
"integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==",
"license": "MIT",
"dependencies": {
"once": "^1.4.0"
}
},
"node_modules/es-define-property": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
@@ -409,15 +308,6 @@
"integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==",
"license": "MIT"
},
"node_modules/expand-template": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz",
"integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==",
"license": "(MIT OR WTFPL)",
"engines": {
"node": ">=6"
}
},
"node_modules/express": {
"version": "4.22.1",
"resolved": "https://registry.npmjs.org/express/-/express-4.22.1.tgz",
@@ -464,12 +354,6 @@
"url": "https://opencollective.com/express"
}
},
"node_modules/file-uri-to-path": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
"integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==",
"license": "MIT"
},
"node_modules/fill-range": {
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
@@ -538,12 +422,6 @@
"node": ">= 0.6"
}
},
"node_modules/fs-constants": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz",
"integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==",
"license": "MIT"
},
"node_modules/function-bind": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
@@ -590,12 +468,6 @@
"node": ">= 0.4"
}
},
"node_modules/github-from-package": {
"version": "0.0.0",
"resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz",
"integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==",
"license": "MIT"
},
"node_modules/gopd": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
@@ -718,38 +590,12 @@
"node": ">=0.10.0"
}
},
"node_modules/ieee754": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
"integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
],
"license": "BSD-3-Clause"
},
"node_modules/inherits": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
"license": "ISC"
},
"node_modules/ini": {
"version": "1.3.8",
"resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
"integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==",
"license": "ISC"
},
"node_modules/ipaddr.js": {
"version": "1.9.1",
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
@@ -798,6 +644,44 @@
"node": ">=0.10.0"
}
},
"node_modules/lru-cache": {
"version": "10.4.3",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz",
"integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
"license": "ISC"
},
"node_modules/mariadb": {
"version": "3.5.2",
"resolved": "https://registry.npmjs.org/mariadb/-/mariadb-3.5.2.tgz",
"integrity": "sha512-9rztrI4nouxAY/82a+RlzzZ5ie2vxu2eYclkBvTy1ATXH1B9cnvZ0O71Pzsy/mlfDb5P3HhOg0JzQKkDRhctyA==",
"license": "LGPL-2.1-or-later",
"dependencies": {
"@types/geojson": "^7946.0.16",
"@types/node": ">=18",
"denque": "^2.1.0",
"iconv-lite": "^0.7.2",
"lru-cache": "^10.4.3"
},
"engines": {
"node": ">= 18"
}
},
"node_modules/mariadb/node_modules/iconv-lite": {
"version": "0.7.2",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz",
"integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==",
"license": "MIT",
"dependencies": {
"safer-buffer": ">= 2.1.2 < 3.0.0"
},
"engines": {
"node": ">=0.10.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/express"
}
},
"node_modules/math-intrinsics": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
@@ -880,45 +764,12 @@
"node": ">= 0.6"
}
},
"node_modules/mimic-response": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz",
"integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==",
"license": "MIT",
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/minimist": {
"version": "1.2.8",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
"integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/mkdirp-classic": {
"version": "0.5.3",
"resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz",
"integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==",
"license": "MIT"
},
"node_modules/ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
"license": "MIT"
},
"node_modules/napi-build-utils": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-2.0.0.tgz",
"integrity": "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==",
"license": "MIT"
},
"node_modules/negotiator": {
"version": "0.6.3",
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
@@ -928,18 +779,6 @@
"node": ">= 0.6"
}
},
"node_modules/node-abi": {
"version": "3.89.0",
"resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.89.0.tgz",
"integrity": "sha512-6u9UwL0HlAl21+agMN3YAMXcKByMqwGx+pq+P76vii5f7hTPtKDp08/H9py6DY+cfDw7kQNTGEj/rly3IgbNQA==",
"license": "MIT",
"dependencies": {
"semver": "^7.3.5"
},
"engines": {
"node": ">=10"
}
},
"node_modules/object-assign": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
@@ -973,15 +812,6 @@
"node": ">= 0.8"
}
},
"node_modules/once": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
"integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
"license": "ISC",
"dependencies": {
"wrappy": "1"
}
},
"node_modules/parseurl": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
@@ -1009,33 +839,6 @@
"url": "https://github.com/sponsors/jonschlinkert"
}
},
"node_modules/prebuild-install": {
"version": "7.1.3",
"resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz",
"integrity": "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==",
"deprecated": "No longer maintained. Please contact the author of the relevant native addon; alternatives are available.",
"license": "MIT",
"dependencies": {
"detect-libc": "^2.0.0",
"expand-template": "^2.0.3",
"github-from-package": "0.0.0",
"minimist": "^1.2.3",
"mkdirp-classic": "^0.5.3",
"napi-build-utils": "^2.0.0",
"node-abi": "^3.3.0",
"pump": "^3.0.0",
"rc": "^1.2.7",
"simple-get": "^4.0.0",
"tar-fs": "^2.0.0",
"tunnel-agent": "^0.6.0"
},
"bin": {
"prebuild-install": "bin.js"
},
"engines": {
"node": ">=10"
}
},
"node_modules/proxy-addr": {
"version": "2.0.7",
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
@@ -1049,16 +852,6 @@
"node": ">= 0.10"
}
},
"node_modules/pump": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/pump/-/pump-3.0.4.tgz",
"integrity": "sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==",
"license": "MIT",
"dependencies": {
"end-of-stream": "^1.1.0",
"once": "^1.3.1"
}
},
"node_modules/qs": {
"version": "6.14.2",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.14.2.tgz",
@@ -1098,35 +891,6 @@
"node": ">= 0.8"
}
},
"node_modules/rc": {
"version": "1.2.8",
"resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
"integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==",
"license": "(BSD-2-Clause OR MIT OR Apache-2.0)",
"dependencies": {
"deep-extend": "^0.6.0",
"ini": "~1.3.0",
"minimist": "^1.2.0",
"strip-json-comments": "~2.0.1"
},
"bin": {
"rc": "cli.js"
}
},
"node_modules/readable-stream": {
"version": "3.6.2",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
"integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
"license": "MIT",
"dependencies": {
"inherits": "^2.0.3",
"string_decoder": "^1.1.1",
"util-deprecate": "^1.0.1"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/requires-port": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
@@ -1159,18 +923,6 @@
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
"license": "MIT"
},
"node_modules/semver": {
"version": "7.7.4",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz",
"integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==",
"license": "ISC",
"bin": {
"semver": "bin/semver.js"
},
"engines": {
"node": ">=10"
}
},
"node_modules/send": {
"version": "0.19.2",
"resolved": "https://registry.npmjs.org/send/-/send-0.19.2.tgz",
@@ -1294,51 +1046,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/simple-concat": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz",
"integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
],
"license": "MIT"
},
"node_modules/simple-get": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz",
"integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
],
"license": "MIT",
"dependencies": {
"decompress-response": "^6.0.0",
"once": "^1.3.1",
"simple-concat": "^1.0.0"
}
},
"node_modules/statuses": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz",
@@ -1348,52 +1055,6 @@
"node": ">= 0.8"
}
},
"node_modules/string_decoder": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
"integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
"license": "MIT",
"dependencies": {
"safe-buffer": "~5.2.0"
}
},
"node_modules/strip-json-comments": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
"integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==",
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/tar-fs": {
"version": "2.1.4",
"resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.4.tgz",
"integrity": "sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ==",
"license": "MIT",
"dependencies": {
"chownr": "^1.1.1",
"mkdirp-classic": "^0.5.2",
"pump": "^3.0.0",
"tar-stream": "^2.1.4"
}
},
"node_modules/tar-stream": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz",
"integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==",
"license": "MIT",
"dependencies": {
"bl": "^4.0.3",
"end-of-stream": "^1.4.1",
"fs-constants": "^1.0.0",
"inherits": "^2.0.3",
"readable-stream": "^3.1.1"
},
"engines": {
"node": ">=6"
}
},
"node_modules/to-regex-range": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
@@ -1415,18 +1076,6 @@
"node": ">=0.6"
}
},
"node_modules/tunnel-agent": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
"integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==",
"license": "Apache-2.0",
"dependencies": {
"safe-buffer": "^5.0.1"
},
"engines": {
"node": "*"
}
},
"node_modules/type-is": {
"version": "1.6.18",
"resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
@@ -1455,12 +1104,6 @@
"node": ">= 0.8"
}
},
"node_modules/util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
"license": "MIT"
},
"node_modules/utils-merge": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
@@ -1478,12 +1121,6 @@
"engines": {
"node": ">= 0.8"
}
},
"node_modules/wrappy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
"license": "ISC"
}
}
}
+1 -2
View File
@@ -10,7 +10,6 @@
"cors": "^2.8.5",
"express": "^4.21.2",
"http-proxy-middleware": "^3.0.5",
"sqlite": "^5.1.7",
"sqlite3": "^5.1.7"
"mariadb": "^3.4.0"
}
}
+1 -1
View File
@@ -52,7 +52,7 @@ function AppContent() {
pacientes={store.pacientes}
internaciones={store.internaciones}
onActualizarCama={store.actualizarCama}
onAgregarCama={store.agregarCama}
onAgregarCama={store.agregarCama as any}
onEliminarCama={store.eliminarCama}
onIniciarInternacion={store.iniciarInternacion}
getPacienteById={store.getPacienteById}
File diff suppressed because it is too large Load Diff
+364 -162
View File
@@ -52,7 +52,7 @@ interface HospitalState {
}
const API_BASE = 'http://localhost:4001/api';
const API_BASE = 'http://localhost:4000/api';
const defaultState = (): HospitalState => ({
pacientes: [],
@@ -111,61 +111,25 @@ export function useHospitalStore() {
return () => { mounted = false; };
}, []);
// Persistence: event-driven + periodic save
const [saveQueue, setSaveQueue] = useState<string[]>([]);
const [isSaving, setIsSaving] = useState(false);
// Track which parts changed
const [changedKeys, setChangedKeys] = useState<Set<string>>(new Set());
// Mark parts as changed
const markChanged = (key: string) => {
setChangedKeys(prev => {
const next = new Set(prev);
next.add(key);
return next;
});
};
// Save only changed parts
const saveChanges = useCallback(async () => {
if (isSaving) return;
setIsSaving(true);
// Helper to make API calls with error handling
const apiCall = useCallback(async (method: string, endpoint: string, data?: any) => {
try {
// If there are marked changed keys, save only those; otherwise save all state
const keysToSave = changedKeys.size > 0 ? Array.from(changedKeys) : Object.keys(state).filter(k => !['currentUser', 'isAuthenticated', 'vistaActual', 'currentInternacionId'].includes(k));
const partialState: any = {};
keysToSave.forEach(key => {
if (key in state) {
partialState[key] = state[key];
}
});
await fetch(`${API_BASE}/state/partial`, {
method: 'PUT',
const res = await fetch(`${API_BASE}${endpoint}`, {
method,
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(partialState),
body: data ? JSON.stringify(data) : undefined,
});
setChangedKeys(new Set());
if (!res.ok) throw new Error(`API error: ${res.statusText}`);
return await res.json().catch(() => ({ ok: true }));
} catch (err) {
console.error('Save error:', err);
} finally {
setIsSaving(false);
console.error(`API call failed: ${method} ${endpoint}`, err);
throw err;
}
}, [state, changedKeys, isSaving]);
}, []);
// Periodic save every 5 seconds if there are changes
useEffect(() => {
if (!isLoaded) return;
const t = setInterval(() => {
saveChanges();
}, 5000);
return () => clearInterval(t);
}, [isLoaded, saveChanges]);
// Also save on特定 events (optional)
// Also save on specific events (optional)
const queueSave = (keys: string[]) => {
keys.forEach(markChanged);
// Reserved for future implementation
};
// Utility functions
@@ -240,35 +204,53 @@ export function useHospitalStore() {
}, []);
// Acciones de pacientes
const agregarPaciente = useCallback((paciente: Omit<Paciente, 'id' | 'fechaRegistro'>) => {
const agregarPaciente = useCallback(async (paciente: Omit<Paciente, 'id' | 'fechaRegistro'>) => {
const nuevoPaciente: Paciente = {
...paciente,
id: generateUUID(),
fechaRegistro: new Date().toISOString().split('T')[0],
};
try {
await apiCall('POST', '/pacientes', nuevoPaciente);
setState(prev => ({
...prev,
pacientes: [...prev.pacientes, nuevoPaciente],
}));
return nuevoPaciente.id;
}, []);
} catch (err) {
console.error('Error al agregar paciente:', err);
throw err;
}
}, [apiCall]);
const actualizarPaciente = useCallback((id: string, datos: Partial<Paciente>) => {
const actualizarPaciente = useCallback(async (id: string, datos: Partial<Paciente>) => {
try {
await apiCall('PUT', `/pacientes/${id}`, datos);
setState(prev => ({
...prev,
pacientes: prev.pacientes.map(p => p.id === id ? { ...p, ...datos } : p),
}));
}, []);
} catch (err) {
console.error('Error al actualizar paciente:', err);
throw err;
}
}, [apiCall]);
const eliminarPaciente = useCallback((id: string) => {
const eliminarPaciente = useCallback(async (id: string) => {
try {
await apiCall('DELETE', `/pacientes/${id}`);
setState(prev => ({
...prev,
pacientes: prev.pacientes.filter(p => p.id !== id),
}));
}, []);
} catch (err) {
console.error('Error al eliminar paciente:', err);
throw err;
}
}, [apiCall]);
// Acciones de camas
const actualizarCama = useCallback((id: string, datos: Partial<Cama>) => {
const actualizarCama = useCallback(async (id: string, datos: Partial<Cama>) => {
const cama = state.camas.find(c => c.id === id);
if (!cama) return;
const user = state.currentUser;
@@ -277,39 +259,70 @@ export function useHospitalStore() {
console.warn('No tiene permisos para actualizar cama en esta área');
return;
}
try {
await apiCall('PUT', `/camas/${id}`, datos);
setState(prev => ({
...prev,
camas: prev.camas.map(c => c.id === id ? { ...c, ...datos } : c),
}));
}, [state.camas, state.currentUser]);
} catch (err) {
console.error('Error al actualizar cama:', err);
throw err;
}
}, [state.camas, state.currentUser, apiCall]);
const agregarCama = useCallback((cama: Omit<Cama, 'id'>) => {
const agregarCama = useCallback(async (cama: Omit<Cama, 'id'>) => {
const nuevaCama: Cama = {
...cama,
id: generateUUID(),
} as Cama;
try {
await apiCall('POST', '/camas', nuevaCama);
setState(prev => ({
...prev,
camas: [...prev.camas, nuevaCama],
}));
return nuevaCama.id;
}, []);
} catch (err) {
console.error('Error al agregar cama:', err);
throw err;
}
}, [apiCall]);
const eliminarCama = useCallback((id: string) => {
const eliminarCama = useCallback(async (id: string) => {
try {
await apiCall('DELETE', `/camas/${id}`);
setState(prev => ({
...prev,
camas: prev.camas.filter(c => c.id !== id),
internaciones: prev.internaciones.map(i => i.camaId === id ? { ...i, activa: false } : i),
}));
}, []);
} catch (err) {
console.error('Error al eliminar cama:', err);
throw err;
}
}, [apiCall]);
// Acciones de internaciones
const iniciarInternacion = useCallback((internacion: Omit<Internacion, 'id' | 'activa'>) => {
const iniciarInternacion = useCallback(async (internacion: Omit<Internacion, 'id' | 'activa'>) => {
const nuevaInternacion: Internacion = {
...internacion,
id: generateUUID(),
activa: true,
};
try {
// API call inmediata para crear internación
await apiCall('POST', '/internaciones', nuevaInternacion);
// Actualizar estado de la cama en la base de datos
await apiCall('PUT', `/camas/${internacion.camaId}`, {
estado: 'Ocupada',
pacienteId: internacion.pacienteId,
internacionId: nuevaInternacion.id
});
// Actualizar estado local después del éxito
setState(prev => ({
...prev,
internaciones: [...prev.internaciones, nuevaInternacion],
@@ -320,14 +333,14 @@ export function useHospitalStore() {
),
}));
// Marcar como cambiadas para el guardado
markChanged('internaciones');
markChanged('camas');
return nuevaInternacion.id;
}, [markChanged]);
} catch (err) {
console.error('Error al crear internación:', err);
throw err;
}
}, [apiCall]);
const finalizarInternacion = useCallback((internacionId: string, datos: {
const finalizarInternacion = useCallback(async (internacionId: string, datos: {
fechaEgreso: string;
diagnosticoEgreso: string;
motivoEgreso: Internacion['motivoEgreso']
@@ -338,7 +351,28 @@ export function useHospitalStore() {
const areaFueraDeArea = state.areas.find(a => (a.nombre || '').trim().toLowerCase() === 'fuera de area');
const areaFueraDeAreaId = areaFueraDeArea?.id;
const esFueraDeArea = internacion.areaId === areaFueraDeAreaId;
const camaId = internacion.camaId;
try {
// API call inmediata para finalizar internación
await apiCall('PUT', `/internaciones/${internacionId}`, { ...datos, activa: false });
// Eliminar o actualizar la cama según el área
if (camaId) {
if (esFueraDeArea) {
// Eliminar la cama si es "Fuera de área"
await apiCall('DELETE', `/camas/${camaId}`);
} else {
// Marcar como disponible si es un área normal
await apiCall('PUT', `/camas/${camaId}`, {
estado: 'Disponible',
pacienteId: null,
internacionId: null
});
}
}
// Actualizar estado local después del éxito
setState(prev => {
return {
...prev,
@@ -347,22 +381,22 @@ export function useHospitalStore() {
? { ...i, ...datos, activa: false }
: i
),
camas: esFueraDeArea
? prev.camas.filter(c => c.id !== internacion.camaId)
cams: esFueraDeArea
? prev.camas.filter(c => c.id !== camaId)
: prev.camas.map(c =>
c.id === internacion.camaId
? { ...c, estado: 'Disponible', pacienteId: undefined, internacionId: undefined }
c.id === camaId
? { ...c, estado: 'Disponible' as const, pacienteId: undefined, internacionId: undefined }
: c
),
};
});
} catch (err) {
console.error('Error al finalizar internación:', err);
throw err;
}
}, [state.areas, apiCall]);
// Marcar como cambiadas para el guardado
markChanged('internaciones');
markChanged('camas');
}, [state.areas, markChanged]);
const actualizarInternacion = useCallback((internacionId: string, datos: Partial<Internacion>) => {
const actualizarInternacion = useCallback(async (internacionId: string, datos: Partial<Internacion>) => {
const internacion = state.internaciones.find(i => i.id === internacionId);
if (!internacion) return;
@@ -382,6 +416,25 @@ export function useHospitalStore() {
const pacienteId = datos.pacienteId || internacion.pacienteId;
try {
// API call inmediata
await apiCall('PUT', `/internaciones/${internacionId}`, datos);
// Si hay cambio de cama, actualizar estados en la base de datos
if (cambioDeCama && newCamaId && oldCamaId) {
await apiCall('PUT', `/camas/${newCamaId}`, {
estado: 'Ocupada',
pacienteId,
internacionId
});
await apiCall('PUT', `/camas/${oldCamaId}`, {
estado: 'Disponible',
pacienteId: null,
internacionId: null
});
}
// Actualizar estado local después del éxito
setState(prev => {
const result = {
...prev,
@@ -402,33 +455,14 @@ export function useHospitalStore() {
};
return result;
});
// Marcar como cambiadas para el guardado
markChanged('internaciones');
if (cambioDeCama) {
markChanged('camas');
} catch (err) {
console.error('Error al actualizar internación:', err);
throw err;
}
if (oldCamaId !== newCamaId && newCamaId && oldCamaId) {
setTimeout(() => {
fetch(`${API_BASE}/camas/${newCamaId}`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ estado: 'Ocupada' }),
}).catch(() => {});
}, 500);
setTimeout(() => {
fetch(`${API_BASE}/camas/${oldCamaId}`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ estado: 'Disponible' }),
}).catch(() => {});
}, 1000);
}
}, [state.internaciones, state.currentUser]);
}, [state.internaciones, state.currentUser, apiCall]);
// Acciones de evoluciones
const agregarEvolucion = useCallback((evolucion: Omit<Evolucion, 'id'>) => {
const agregarEvolucion = useCallback(async (evolucion: Omit<Evolucion, 'id'>) => {
const internacion = state.internaciones.find(i => i.id === evolucion.internacionId);
const areaId = internacion?.areaId || null;
if (!canAccessArea(areaId)) {
@@ -439,16 +473,21 @@ export function useHospitalStore() {
...evolucion,
id: generateUUID(),
};
try {
await apiCall('POST', '/evoluciones', nuevaEvolucion);
setState(prev => ({
...prev,
evoluciones: [...prev.evoluciones, nuevaEvolucion],
}));
markChanged('evoluciones');
return nuevaEvolucion.id;
}, [state.internaciones, state.currentUser, markChanged]);
} catch (err) {
console.error('Error al agregar evolución:', err);
throw err;
}
}, [state.internaciones, state.currentUser, apiCall]);
const eliminarEvolucion = useCallback((id: string) => {
const eliminarEvolucion = useCallback(async (id: string) => {
const evolucion = state.evoluciones.find(e => e.id === id);
if (!evolucion) return;
const internacion = state.internaciones.find(i => i.id === evolucion.internacionId);
@@ -457,14 +496,20 @@ export function useHospitalStore() {
console.warn('No tiene permisos para eliminar evolución en esta área');
return;
}
try {
await apiCall('DELETE', `/evoluciones/${id}`);
setState(prev => ({
...prev,
evoluciones: prev.evoluciones.filter(e => e.id !== id),
}));
markChanged('evoluciones');
}, [markChanged]);
} catch (err) {
console.error('Error al eliminar evolución:', err);
throw err;
}
}, [state.internaciones, state.currentUser, apiCall]);
const actualizarEvolucion = useCallback((id: string, datos: Partial<Evolucion>) => {
const actualizarEvolucion = useCallback(async (id: string, datos: Partial<Evolucion>) => {
const evolucion = state.evoluciones.find(e => e.id === id);
if (!evolucion) return;
const internacion = state.internaciones.find(i => i.id === evolucion.internacionId);
@@ -473,15 +518,21 @@ export function useHospitalStore() {
console.warn('No tiene permisos para actualizar evolución en esta área');
return;
}
try {
await apiCall('PUT', `/evoluciones/${id}`, datos);
setState(prev => ({
...prev,
evoluciones: prev.evoluciones.map(e => e.id === id ? { ...e, ...datos } : e),
}));
markChanged('evoluciones');
}, [state.internaciones, state.currentUser, markChanged]);
} catch (err) {
console.error('Error al actualizar evolución:', err);
throw err;
}
}, [state.internaciones, state.currentUser, apiCall]);
// Acciones de laboratorios
const agregarLaboratorio = useCallback((laboratorio: Omit<Laboratorio, 'id'>) => {
const agregarLaboratorio = useCallback(async (laboratorio: Omit<Laboratorio, 'id'>) => {
const internacion = state.internaciones.find(i => i.id === laboratorio.internacionId);
const areaId = internacion?.areaId || null;
if (!canAccessArea(areaId)) {
@@ -492,14 +543,20 @@ export function useHospitalStore() {
...laboratorio,
id: generateUUID(),
};
try {
await apiCall('POST', '/laboratorios', nuevoLaboratorio);
setState(prev => ({
...prev,
laboratorios: [...prev.laboratorios, nuevoLaboratorio],
}));
return nuevoLaboratorio.id;
}, [state.internaciones, state.currentUser]);
} catch (err) {
console.error('Error al agregar laboratorio:', err);
throw err;
}
}, [state.internaciones, state.currentUser, apiCall]);
const eliminarLaboratorio = useCallback((id: string) => {
const eliminarLaboratorio = useCallback(async (id: string) => {
const laboratorio = state.laboratorios.find(l => l.id === id);
if (!laboratorio) return;
const internacion = state.internaciones.find(i => i.id === laboratorio.internacionId);
@@ -508,13 +565,19 @@ export function useHospitalStore() {
console.warn('No tiene permisos para eliminar laboratorio en esta área');
return;
}
try {
await apiCall('DELETE', `/laboratorios/${id}`);
setState(prev => ({
...prev,
laboratorios: prev.laboratorios.filter(l => l.id !== id),
}));
}, [state.internaciones, state.currentUser]);
} catch (err) {
console.error('Error al eliminar laboratorio:', err);
throw err;
}
}, [state.internaciones, state.currentUser, apiCall]);
const actualizarLaboratorio = useCallback((id: string, datos: Partial<Laboratorio>) => {
const actualizarLaboratorio = useCallback(async (id: string, datos: Partial<Laboratorio>) => {
const laboratorio = state.laboratorios.find(l => l.id === id);
if (!laboratorio) return;
const internacion = state.internaciones.find(i => i.id === laboratorio.internacionId);
@@ -523,14 +586,20 @@ export function useHospitalStore() {
console.warn('No tiene permisos para actualizar laboratorio en esta área');
return;
}
try {
await apiCall('PUT', `/laboratorios/${id}`, datos);
setState(prev => ({
...prev,
laboratorios: prev.laboratorios.map(l => l.id === id ? { ...l, ...datos } : l),
}));
}, [state.internaciones, state.currentUser]);
} catch (err) {
console.error('Error al actualizar laboratorio:', err);
throw err;
}
}, [state.internaciones, state.currentUser, apiCall]);
// Acciones de ácido-base
const agregarAcidoBase = useCallback((acidoBase: Omit<AcidoBase, 'id'>) => {
const agregarAcidoBase = useCallback(async (acidoBase: Omit<AcidoBase, 'id'>) => {
const internacion = state.internaciones.find(i => i.id === acidoBase.internacionId);
const areaId = internacion?.areaId || null;
if (!canAccessArea(areaId)) {
@@ -541,14 +610,20 @@ export function useHospitalStore() {
...acidoBase,
id: generateUUID(),
};
try {
await apiCall('POST', '/acid-os-base', nuevoAcidoBase);
setState(prev => ({
...prev,
acidosBase: [...prev.acidosBase, nuevoAcidoBase],
}));
return nuevoAcidoBase.id;
}, [state.internaciones, state.currentUser]);
} catch (err) {
console.error('Error al agregar ácido-base:', err);
throw err;
}
}, [state.internaciones, state.currentUser, apiCall]);
const actualizarAcidoBase = useCallback((id: string, datos: Partial<AcidoBase>) => {
const actualizarAcidoBase = useCallback(async (id: string, datos: Partial<AcidoBase>) => {
const acidoBase = state.acidosBase.find(a => a.id === id);
if (!acidoBase) return;
const internacion = state.internaciones.find(i => i.id === acidoBase.internacionId);
@@ -557,13 +632,19 @@ export function useHospitalStore() {
console.warn('No tiene permisos para actualizar ácido-base en esta área');
return;
}
try {
await apiCall('PUT', `/acid-os-base/${id}`, datos);
setState(prev => ({
...prev,
acidosBase: prev.acidosBase.map(a => a.id === id ? { ...a, ...datos } : a),
}));
}, [state.internaciones, state.currentUser]);
} catch (err) {
console.error('Error al actualizar ácido-base:', err);
throw err;
}
}, [state.internaciones, state.currentUser, apiCall]);
const eliminarAcidoBase = useCallback((id: string) => {
const eliminarAcidoBase = useCallback(async (id: string) => {
const acidoBase = state.acidosBase.find(a => a.id === id);
if (!acidoBase) return;
const internacion = state.internaciones.find(i => i.id === acidoBase.internacionId);
@@ -572,14 +653,20 @@ export function useHospitalStore() {
console.warn('No tiene permisos para eliminar ácido-base en esta área');
return;
}
try {
await apiCall('DELETE', `/acid-os-base/${id}`);
setState(prev => ({
...prev,
acidosBase: prev.acidosBase.filter(a => a.id !== id),
}));
}, [state.internaciones, state.currentUser]);
} catch (err) {
console.error('Error al eliminar ácido-base:', err);
throw err;
}
}, [state.internaciones, state.currentUser, apiCall]);
// Acciones de cultivos
const agregarCultivo = useCallback((cultivo: Omit<Cultivo, 'id'>) => {
const agregarCultivo = useCallback(async (cultivo: Omit<Cultivo, 'id'>) => {
const internacion = state.internaciones.find(i => i.id === cultivo.internacionId);
const areaId = internacion?.areaId || null;
if (!canAccessArea(areaId)) {
@@ -590,14 +677,20 @@ export function useHospitalStore() {
...cultivo,
id: generateUUID(),
};
try {
await apiCall('POST', '/cultivos', nuevoCultivo);
setState(prev => ({
...prev,
cultivos: [...prev.cultivos, nuevoCultivo],
}));
return nuevoCultivo.id;
}, [state.internaciones, state.currentUser]);
} catch (err) {
console.error('Error al agregar cultivo:', err);
throw err;
}
}, [state.internaciones, state.currentUser, apiCall]);
const actualizarCultivo = useCallback((id: string, datos: Partial<Cultivo>) => {
const actualizarCultivo = useCallback(async (id: string, datos: Partial<Cultivo>) => {
const cultivo = state.cultivos.find(c => c.id === id);
if (!cultivo) return;
const internacion = state.internaciones.find(i => i.id === cultivo.internacionId);
@@ -606,13 +699,19 @@ export function useHospitalStore() {
console.warn('No tiene permisos para actualizar cultivo en esta área');
return;
}
try {
await apiCall('PUT', `/cultivos/${id}`, datos);
setState(prev => ({
...prev,
cultivos: prev.cultivos.map(c => c.id === id ? { ...c, ...datos } : c),
}));
}, [state.internaciones, state.currentUser]);
} catch (err) {
console.error('Error al actualizar cultivo:', err);
throw err;
}
}, [state.internaciones, state.currentUser, apiCall]);
const eliminarCultivo = useCallback((id: string) => {
const eliminarCultivo = useCallback(async (id: string) => {
const cultivo = state.cultivos.find(c => c.id === id);
if (!cultivo) return;
const internacion = state.internaciones.find(i => i.id === cultivo.internacionId);
@@ -621,14 +720,20 @@ export function useHospitalStore() {
console.warn('No tiene permisos para eliminar cultivo en esta área');
return;
}
try {
await apiCall('DELETE', `/cultivos/${id}`);
setState(prev => ({
...prev,
cultivos: prev.cultivos.filter(c => c.id !== id),
}));
}, [state.internaciones, state.currentUser]);
} catch (err) {
console.error('Error al eliminar cultivo:', err);
throw err;
}
}, [state.internaciones, state.currentUser, apiCall]);
// Acciones de estudios complementarios
const agregarEstudioComplementario = useCallback((estudio: Omit<EstudioComplementario, 'id'>) => {
const agregarEstudioComplementario = useCallback(async (estudio: Omit<EstudioComplementario, 'id'>) => {
const internacion = state.internaciones.find(i => i.id === estudio.internacionId);
const areaId = internacion?.areaId || null;
if (!canAccessArea(areaId)) {
@@ -639,14 +744,20 @@ export function useHospitalStore() {
...estudio,
id: generateUUID(),
};
try {
await apiCall('POST', '/estudios-complementarios', nuevoEstudio);
setState(prev => ({
...prev,
estudiosComplementarios: [...prev.estudiosComplementarios, nuevoEstudio],
}));
return nuevoEstudio.id;
}, [state.internaciones, state.currentUser]);
} catch (err) {
console.error('Error al agregar estudio:', err);
throw err;
}
}, [state.internaciones, state.currentUser, apiCall]);
const actualizarEstudioComplementario = useCallback((id: string, datos: Partial<EstudioComplementario>) => {
const actualizarEstudioComplementario = useCallback(async (id: string, datos: Partial<EstudioComplementario>) => {
const estudio = state.estudiosComplementarios.find(e => e.id === id);
if (!estudio) return;
const internacion = state.internaciones.find(i => i.id === estudio.internacionId);
@@ -655,13 +766,19 @@ export function useHospitalStore() {
console.warn('No tiene permisos para actualizar estudio complementario en esta área');
return;
}
try {
await apiCall('PUT', `/estudios-complementarios/${id}`, datos);
setState(prev => ({
...prev,
estudiosComplementarios: prev.estudiosComplementarios.map(e => e.id === id ? { ...e, ...datos } : e),
}));
}, [state.internaciones, state.currentUser]);
} catch (err) {
console.error('Error al actualizar estudio:', err);
throw err;
}
}, [state.internaciones, state.currentUser, apiCall]);
const eliminarEstudioComplementario = useCallback((id: string) => {
const eliminarEstudioComplementario = useCallback(async (id: string) => {
const estudio = state.estudiosComplementarios.find(e => e.id === id);
if (!estudio) return;
const internacion = state.internaciones.find(i => i.id === estudio.internacionId);
@@ -670,14 +787,20 @@ export function useHospitalStore() {
console.warn('No tiene permisos para eliminar estudio complementario en esta área');
return;
}
try {
await apiCall('DELETE', `/estudios-complementarios/${id}`);
setState(prev => ({
...prev,
estudiosComplementarios: prev.estudiosComplementarios.filter(e => e.id !== id),
}));
}, [state.internaciones, state.currentUser]);
} catch (err) {
console.error('Error al eliminar estudio:', err);
throw err;
}
}, [state.internaciones, state.currentUser, apiCall]);
// Acciones de interconsultas
const agregarInterconsulta = useCallback((interconsulta: Omit<Interconsulta, 'id'>) => {
const agregarInterconsulta = useCallback(async (interconsulta: Omit<Interconsulta, 'id'>) => {
const internacion = state.internaciones.find(i => i.id === interconsulta.internacionId);
const areaId = internacion?.areaId || null;
if (!canAccessArea(areaId)) {
@@ -685,14 +808,20 @@ export function useHospitalStore() {
return null;
}
const nuevaInterconsulta: Interconsulta = { ...interconsulta, id: generateUUID() };
try {
await apiCall('POST', '/interconsultas', nuevaInterconsulta);
setState(prev => ({
...prev,
interconsultas: [...(prev.interconsultas || []), nuevaInterconsulta],
}));
return nuevaInterconsulta.id;
}, [state.internaciones, state.currentUser]);
} catch (err) {
console.error('Error al agregar interconsulta:', err);
throw err;
}
}, [state.internaciones, state.currentUser, apiCall]);
const actualizarInterconsulta = useCallback((id: string, datos: Partial<Interconsulta>) => {
const actualizarInterconsulta = useCallback(async (id: string, datos: Partial<Interconsulta>) => {
const interconsulta = (state.interconsultas || []).find(ic => ic.id === id);
if (!interconsulta) return;
const internacion = state.internaciones.find(i => i.id === interconsulta.internacionId);
@@ -701,13 +830,19 @@ export function useHospitalStore() {
console.warn('No tiene permisos para actualizar interconsulta en esta área');
return;
}
try {
await apiCall('PUT', `/interconsultas/${id}`, datos);
setState(prev => ({
...prev,
interconsultas: (prev.interconsultas || []).map(ic => ic.id === id ? { ...ic, ...datos } : ic),
}));
}, [state.internaciones, state.currentUser]);
} catch (err) {
console.error('Error al actualizar interconsulta:', err);
throw err;
}
}, [state.internaciones, state.currentUser, apiCall]);
const eliminarInterconsulta = useCallback((id: string) => {
const eliminarInterconsulta = useCallback(async (id: string) => {
const interconsulta = (state.interconsultas || []).find(ic => ic.id === id);
if (!interconsulta) return;
const internacion = state.internaciones.find(i => i.id === interconsulta.internacionId);
@@ -716,13 +851,19 @@ export function useHospitalStore() {
console.warn('No tiene permisos para eliminar interconsulta en esta área');
return;
}
try {
await apiCall('DELETE', `/interconsultas/${id}`);
setState(prev => ({
...prev,
interconsultas: (prev.interconsultas || []).filter(ic => ic.id !== id),
}));
}, [state.internaciones, state.currentUser]);
} catch (err) {
console.error('Error al eliminar interconsulta:', err);
throw err;
}
}, [state.internaciones, state.currentUser, apiCall]);
const agregarATB = useCallback((atb: Omit<ATB, 'id'>) => {
const agregarATB = useCallback(async (atb: Omit<ATB, 'id'>) => {
const internacion = state.internaciones.find(i => i.id === atb.internacionId);
const areaId = internacion?.areaId || null;
if (!canAccessArea(areaId)) {
@@ -730,14 +871,20 @@ export function useHospitalStore() {
return null;
}
const nuevoATB: ATB = { ...atb, id: generateUUID() };
try {
await apiCall('POST', '/atb', nuevoATB);
setState(prev => ({
...prev,
atb: [...prev.atb, nuevoATB],
}));
return nuevoATB.id;
}, [state.internaciones, state.currentUser]);
} catch (err) {
console.error('Error al agregar ATB:', err);
throw err;
}
}, [state.internaciones, state.currentUser, apiCall]);
const actualizarATB = useCallback((id: string, datos: Partial<ATB>) => {
const actualizarATB = useCallback(async (id: string, datos: Partial<ATB>) => {
const atb = state.atb.find(a => a.id === id);
if (!atb) return;
const internacion = state.internaciones.find(i => i.id === atb.internacionId);
@@ -746,13 +893,19 @@ export function useHospitalStore() {
console.warn('No tiene permisos para actualizar ATB en esta área');
return;
}
try {
await apiCall('PUT', `/atb/${id}`, datos);
setState(prev => ({
...prev,
atb: prev.atb.map(a => a.id === id ? { ...a, ...datos } : a),
}));
}, [state.internaciones, state.currentUser]);
} catch (err) {
console.error('Error al actualizar ATB:', err);
throw err;
}
}, [state.internaciones, state.currentUser, apiCall]);
const eliminarATB = useCallback((id: string) => {
const eliminarATB = useCallback(async (id: string) => {
const atb = state.atb.find(a => a.id === id);
if (!atb) return;
const internacion = state.internaciones.find(i => i.id === atb.internacionId);
@@ -761,13 +914,19 @@ export function useHospitalStore() {
console.warn('No tiene permisos para eliminar ATB en esta área');
return;
}
try {
await apiCall('DELETE', `/atb/${id}`);
setState(prev => ({
...prev,
atb: prev.atb.filter(a => a.id !== id),
}));
}, [state.internaciones, state.currentUser]);
} catch (err) {
console.error('Error al eliminar ATB:', err);
throw err;
}
}, [state.internaciones, state.currentUser, apiCall]);
const agregarIndicacion = useCallback((indicacion: Omit<Indicacion, 'id'>) => {
const agregarIndicacion = useCallback(async (indicacion: Omit<Indicacion, 'id'>) => {
const internacion = state.internaciones.find(i => i.id === indicacion.internacionId);
const areaId = internacion?.areaId || null;
if (!canAccessArea(areaId)) {
@@ -775,14 +934,20 @@ export function useHospitalStore() {
return null;
}
const nuevaIndicacion: Indicacion = { ...indicacion, id: generateUUID() };
try {
await apiCall('POST', '/indicaciones', nuevaIndicacion);
setState(prev => ({
...prev,
indicaciones: [...prev.indicaciones, nuevaIndicacion],
}));
return nuevaIndicacion.id;
}, [state.internaciones, state.currentUser]);
} catch (err) {
console.error('Error al agregar indicación:', err);
throw err;
}
}, [state.internaciones, state.currentUser, apiCall]);
const actualizarIndicacion = useCallback((id: string, datos: Partial<Indicacion>) => {
const actualizarIndicacion = useCallback(async (id: string, datos: Partial<Indicacion>) => {
const indicacion = state.indicaciones.find(i => i.id === id);
if (!indicacion) return;
const internacion = state.internaciones.find(i => i.id === indicacion.internacionId);
@@ -791,13 +956,19 @@ export function useHospitalStore() {
console.warn('No tiene permisos para actualizar indicación en esta área');
return;
}
try {
await apiCall('PUT', `/indicaciones/${id}`, datos);
setState(prev => ({
...prev,
indicaciones: prev.indicaciones.map(i => i.id === id ? { ...i, ...datos } : i),
}));
}, [state.internaciones, state.currentUser]);
} catch (err) {
console.error('Error al actualizar indicación:', err);
throw err;
}
}, [state.internaciones, state.currentUser, apiCall]);
const eliminarIndicacion = useCallback((id: string) => {
const eliminarIndicacion = useCallback(async (id: string) => {
const indicacion = state.indicaciones.find(i => i.id === id);
if (!indicacion) return;
const internacion = state.internaciones.find(i => i.id === indicacion.internacionId);
@@ -806,24 +977,37 @@ export function useHospitalStore() {
console.warn('No tiene permisos para eliminar indicación en esta área');
return;
}
try {
await apiCall('DELETE', `/indicaciones/${id}`);
setState(prev => ({
...prev,
indicaciones: prev.indicaciones.filter(i => i.id !== id),
}));
}, [state.internaciones, state.currentUser]);
} catch (err) {
console.error('Error al eliminar indicación:', err);
throw err;
}
}, [state.internaciones, state.currentUser, apiCall]);
const agregarMovimientoIndicacion = useCallback((movimiento: any) => {
const agregarMovimientoIndicacion = useCallback(async (movimiento: any) => {
const internacion = state.internaciones.find(i => i.id === movimiento.internacionId);
const areaId = internacion?.areaId || null;
if (!canAccessArea(areaId)) {
console.warn('No tiene permisos para agregar movimiento de indicación en esta área');
return;
}
const nuevoMovimiento = { ...movimiento, id: generateUUID() };
try {
await apiCall('POST', '/movimientos-indicaciones', nuevoMovimiento);
setState(prev => ({
...prev,
movimientosIndicaciones: [...(prev.movimientosIndicaciones || []), { ...movimiento, id: generateUUID() }],
movimientosIndicaciones: [...(prev.movimientosIndicaciones || []), nuevoMovimiento],
}));
}, [state.internaciones, state.currentUser]);
} catch (err) {
console.error('Error al agregar movimiento:', err);
throw err;
}
}, [state.internaciones, state.currentUser, apiCall]);
const getMovimientosByInternacion = useCallback((internacionId: string) => {
return (state.movimientosIndicaciones || [])
@@ -832,29 +1016,47 @@ export function useHospitalStore() {
}, [state.movimientosIndicaciones]);
// Acciones de areas
const agregarArea = useCallback((area: Omit<Area, 'id'>) => {
const agregarArea = useCallback(async (area: Omit<Area, 'id'>) => {
const nuevaArea: Area = { ...area, id: generateUUID() };
try {
await apiCall('POST', '/areas', nuevaArea);
setState(prev => ({
...prev,
areas: [...prev.areas, nuevaArea],
}));
return nuevaArea.id;
}, []);
} catch (err) {
console.error('Error al agregar área:', err);
throw err;
}
}, [apiCall]);
const actualizarArea = useCallback((id: string, datos: Partial<Area>) => {
const actualizarArea = useCallback(async (id: string, datos: Partial<Area>) => {
try {
await apiCall('PUT', `/areas/${id}`, datos);
setState(prev => ({
...prev,
areas: prev.areas.map(a => a.id === id ? { ...a, ...datos } : a),
}));
}, []);
} catch (err) {
console.error('Error al actualizar área:', err);
throw err;
}
}, [apiCall]);
const eliminarArea = useCallback((id: string) => {
const eliminarArea = useCallback(async (id: string) => {
try {
await apiCall('DELETE', `/areas/${id}`);
setState(prev => ({
...prev,
areas: prev.areas.filter(a => a.id !== id),
camas: prev.camas.map(c => c.areaId === id ? { ...c, areaId: undefined } : c),
}));
}, []);
} catch (err) {
console.error('Error al eliminar área:', err);
throw err;
}
}, [apiCall]);
// Funciones de utilidad para consultas
const getPacienteById = useCallback((id: string) => {
+1 -1
View File
@@ -107,7 +107,7 @@ export function Cultivos({
};
const guardarNuevo = () => {
if (!protocolo.trim()) return;
if (!protocolo.trim() || !patient) return;
onAgregarCultivo({
pacienteId: patient.id,
internacionId,
+1 -1
View File
@@ -16,7 +16,7 @@ interface EditIngresoProps {
camas: Cama[];
areas: Area[];
onActualizarInternacion: (id: string, datos: Partial<Internacion>) => void;
onAgregarCama?: (cama: Omit<Cama, 'id'>) => string;
onAgregarCama?: (cama: Omit<Cama, 'id'>) => any;
onVolver: () => void;
getAreaName: (areaId: string | undefined) => string;
}
+3 -3
View File
@@ -17,10 +17,10 @@ interface MapaCamasProps {
pacientes: Paciente[];
internaciones: Internacion[];
onActualizarCama: (id: string, datos: Partial<Cama>) => void;
onAgregarCama: (cama: Omit<Cama, 'id'>) => string;
onAgregarCama: (cama: Omit<Cama, 'id'>) => any;
onEliminarCama: (id: string) => void;
onIniciarInternacion: (internacion: Omit<Internacion, 'id' | 'activa'>) => void;
onAgregarArea: (area: Omit<Area, 'id'>) => string;
onIniciarInternacion: (internacion: Omit<Internacion, 'id' | 'activa'>) => Promise<string>;
onAgregarArea: (area: Omit<Area, 'id'>) => any;
onActualizarArea: (id: string, datos: Partial<Area>) => void;
onEliminarArea: (id: string) => void;
getPacienteById: (id: string) => Paciente | undefined;
+9 -4
View File
@@ -12,8 +12,8 @@ interface NuevoIngresoProps {
pacientes: Paciente[];
camas: Cama[];
areas: Area[];
onIniciarInternacion: (internacion: Omit<Internacion, 'id' | 'activa'>) => void;
onAgregarCama?: (cama: Omit<Cama, 'id'>) => string;
onIniciarInternacion: (internacion: Omit<Internacion, 'id' | 'activa'>) => Promise<string>;
onAgregarCama?: (cama: Omit<Cama, 'id'>) => Promise<string>;
onAgregarPaciente?: (paciente: Omit<Paciente, 'id'>) => void;
onActualizarPaciente?: (id: string, datos: Partial<Paciente>) => void;
onVolver: () => void;
@@ -125,14 +125,19 @@ export function NuevoIngreso({ pacientes, camas, areas, onIniciarInternacion, on
camaId = camaExistente.id;
newAreaId = camaExistente.areaId || '';
} else if (onAgregarCama) {
const nuevaCamaId = onAgregarCama({
try {
camaId = await onAgregarCama({
numero: numeroCama,
areaId: areaFueraDeAreaId,
tipo: 'General',
estado: 'Ocupada'
});
camaId = nuevaCamaId;
newAreaId = areaFueraDeAreaId;
} catch (err) {
console.error('Error al crear cama:', err);
alert('Error al crear la cama');
return;
}
} else {
alert('No se puede agregar una nueva cama');
return;