diff --git a/package-lock.json b/package-lock.json index da4210ee0b5..427a412ce5c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -36,7 +36,7 @@ "browserslist-useragent-regexp": "^4.1.3", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", - "cmdk": "^1.0.0", + "cmdk": "^1.0.4", "cross-env": "^7.0.3", "cypress": "^13.16.1", "dayjs": "^1.11.13", @@ -46,8 +46,7 @@ "hi-profiles": "^1.1.0", "i18next": "^23.16.4", "i18next-browser-languagedetector": "^8.0.0", - "i18next-http-backend": "^2.6.2", - "lodash-es": "^4.17.21", + "i18next-http-backend": "^3.0.1", "next-themes": "^0.4.4", "postcss-loader": "^8.1.1", "qrcode.react": "^4.1.0", @@ -56,7 +55,7 @@ "react-copy-to-clipboard": "^5.1.0", "react-dom": "18.3.1", "react-google-recaptcha": "^3.1.0", - "react-i18next": "^15.1.1", + "react-i18next": "^15.1.3", "react-infinite-scroll-component": "^6.1.0", "react-pdf": "^9.1.1", "react-webcam": "^7.2.0", @@ -76,7 +75,6 @@ "@types/events": "^3.0.3", "@types/google.maps": "^3.58.1", "@types/jsdom": "^21.1.7", - "@types/lodash-es": "^4.17.12", "@types/node": "^22.9.0", "@types/react": "^18.3.12", "@types/react-copy-to-clipboard": "^5.0.7", @@ -5609,23 +5607,6 @@ "optional": true, "peer": true }, - "node_modules/@types/lodash": { - "version": "4.17.11", - "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.11.tgz", - "integrity": "sha512-jzqWo/uQP/iqeGGTjhgFp2yaCrCYTauASQcpdzESNCkHjSprBJVcZP9KG9aQ0q+xcsXiKd/iuw/4dLjS3Odc7Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/lodash-es": { - "version": "4.17.12", - "resolved": "https://registry.npmjs.org/@types/lodash-es/-/lodash-es-4.17.12.tgz", - "integrity": "sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/lodash": "*" - } - }, "node_modules/@types/mdast": { "version": "3.0.15", "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.15.tgz", @@ -7457,363 +7438,19 @@ } }, "node_modules/cmdk": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/cmdk/-/cmdk-1.0.0.tgz", - "integrity": "sha512-gDzVf0a09TvoJ5jnuPvygTB77+XdOSwEmJ88L6XPFPlv7T3RxbP9jgenfylrAMD0+Le1aO0nVjQUzl2g+vjz5Q==", - "dependencies": { - "@radix-ui/react-dialog": "1.0.5", - "@radix-ui/react-primitive": "1.0.3" - }, - "peerDependencies": { - "react": "^18.0.0", - "react-dom": "^18.0.0" - } - }, - "node_modules/cmdk/node_modules/@radix-ui/primitive": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.0.1.tgz", - "integrity": "sha512-yQ8oGX2GVsEYMWGxcovu1uGWPCxV5BFfeeYxqPmuAzUyLT9qmaMXSAhXpb0WrspIeqYzdJpkh2vHModJPgRIaw==", - "dependencies": { - "@babel/runtime": "^7.13.10" - } - }, - "node_modules/cmdk/node_modules/@radix-ui/react-compose-refs": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.0.1.tgz", - "integrity": "sha512-fDSBgd44FKHa1FRMU59qBMPFcl2PZE+2nmqunj+BWFyYYjnhIDWL2ItDs3rrbJDQOtzt5nIebLCQc4QRfz6LJw==", - "dependencies": { - "@babel/runtime": "^7.13.10" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/cmdk/node_modules/@radix-ui/react-context": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.0.1.tgz", - "integrity": "sha512-ebbrdFoYTcuZ0v4wG5tedGnp9tzcV8awzsxYph7gXUyvnNLuTIcCk1q17JEbnVhXAKG9oX3KtchwiMIAYp9NLg==", - "dependencies": { - "@babel/runtime": "^7.13.10" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/cmdk/node_modules/@radix-ui/react-dialog": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.0.5.tgz", - "integrity": "sha512-GjWJX/AUpB703eEBanuBnIWdIXg6NvJFCXcNlSZk4xdszCdhrJgBoUd1cGk67vFO+WdA2pfI/plOpqz/5GUP6Q==", - "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/primitive": "1.0.1", - "@radix-ui/react-compose-refs": "1.0.1", - "@radix-ui/react-context": "1.0.1", - "@radix-ui/react-dismissable-layer": "1.0.5", - "@radix-ui/react-focus-guards": "1.0.1", - "@radix-ui/react-focus-scope": "1.0.4", - "@radix-ui/react-id": "1.0.1", - "@radix-ui/react-portal": "1.0.4", - "@radix-ui/react-presence": "1.0.1", - "@radix-ui/react-primitive": "1.0.3", - "@radix-ui/react-slot": "1.0.2", - "@radix-ui/react-use-controllable-state": "1.0.1", - "aria-hidden": "^1.1.1", - "react-remove-scroll": "2.5.5" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/cmdk/node_modules/@radix-ui/react-dismissable-layer": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.0.5.tgz", - "integrity": "sha512-aJeDjQhywg9LBu2t/At58hCvr7pEm0o2Ke1x33B+MhjNmmZ17sy4KImo0KPLgsnc/zN7GPdce8Cnn0SWvwZO7g==", - "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/primitive": "1.0.1", - "@radix-ui/react-compose-refs": "1.0.1", - "@radix-ui/react-primitive": "1.0.3", - "@radix-ui/react-use-callback-ref": "1.0.1", - "@radix-ui/react-use-escape-keydown": "1.0.3" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/cmdk/node_modules/@radix-ui/react-focus-guards": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.0.1.tgz", - "integrity": "sha512-Rect2dWbQ8waGzhMavsIbmSVCgYxkXLxxR3ZvCX79JOglzdEy4JXMb98lq4hPxUbLr77nP0UOGf4rcMU+s1pUA==", - "dependencies": { - "@babel/runtime": "^7.13.10" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/cmdk/node_modules/@radix-ui/react-focus-scope": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.0.4.tgz", - "integrity": "sha512-sL04Mgvf+FmyvZeYfNu1EPAaaxD+aw7cYeIB9L9Fvq8+urhltTRaEo5ysKOpHuKPclsZcSUMKlN05x4u+CINpA==", - "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-compose-refs": "1.0.1", - "@radix-ui/react-primitive": "1.0.3", - "@radix-ui/react-use-callback-ref": "1.0.1" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/cmdk/node_modules/@radix-ui/react-id": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.0.1.tgz", - "integrity": "sha512-tI7sT/kqYp8p96yGWY1OAnLHrqDgzHefRBKQ2YAkBS5ja7QLcZ9Z/uY7bEjPUatf8RomoXM8/1sMj1IJaE5UzQ==", - "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-use-layout-effect": "1.0.1" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/cmdk/node_modules/@radix-ui/react-portal": { "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.0.4.tgz", - "integrity": "sha512-Qki+C/EuGUVCQTOTD5vzJzJuMUlewbzuKyUy+/iHM2uwGiru9gZeBJtHAPKAEkB5KWGi9mP/CHKcY0wt1aW45Q==", - "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-primitive": "1.0.3" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/cmdk/node_modules/@radix-ui/react-presence": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.0.1.tgz", - "integrity": "sha512-UXLW4UAbIY5ZjcvzjfRFo5gxva8QirC9hF7wRE4U5gz+TP0DbRk+//qyuAQ1McDxBt1xNMBTaciFGvEmJvAZCg==", - "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-compose-refs": "1.0.1", - "@radix-ui/react-use-layout-effect": "1.0.1" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/cmdk/node_modules/@radix-ui/react-primitive": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-1.0.3.tgz", - "integrity": "sha512-yi58uVyoAcK/Nq1inRY56ZSjKypBNKTa/1mcL8qdl6oJeEaDbOldlzrGn7P6Q3Id5d+SYNGc5AJgc4vGhjs5+g==", - "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-slot": "1.0.2" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/cmdk/node_modules/@radix-ui/react-slot": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.0.2.tgz", - "integrity": "sha512-YeTpuq4deV+6DusvVUW4ivBgnkHwECUu0BiN43L5UCDFgdhsRUWAghhTF5MbvNTPzmiFOx90asDSUjWuCNapwg==", - "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-compose-refs": "1.0.1" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/cmdk/node_modules/@radix-ui/react-use-callback-ref": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.0.1.tgz", - "integrity": "sha512-D94LjX4Sp0xJFVaoQOd3OO9k7tpBYNOXdVhkltUbGv2Qb9OXdrg/CpsjlZv7ia14Sylv398LswWBVVu5nqKzAQ==", - "dependencies": { - "@babel/runtime": "^7.13.10" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/cmdk/node_modules/@radix-ui/react-use-controllable-state": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.0.1.tgz", - "integrity": "sha512-Svl5GY5FQeN758fWKrjM6Qb7asvXeiZltlT4U2gVfl8Gx5UAv2sMR0LWo8yhsIZh2oQ0eFdZ59aoOOMV7b47VA==", - "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-use-callback-ref": "1.0.1" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/cmdk/node_modules/@radix-ui/react-use-escape-keydown": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.0.3.tgz", - "integrity": "sha512-vyL82j40hcFicA+M4Ex7hVkB9vHgSse1ZWomAqV2Je3RleKGO5iM8KMOEtfoSB0PnIelMd2lATjTGMYqN5ylTg==", - "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-use-callback-ref": "1.0.1" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/cmdk/node_modules/@radix-ui/react-use-layout-effect": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.0.1.tgz", - "integrity": "sha512-v/5RegiJWYdoCvMnITBkNNx6bCj20fiaJnWtRkU18yITptraXjffz5Qbn05uOiQnOvi+dbkznkoaMltz1GnszQ==", - "dependencies": { - "@babel/runtime": "^7.13.10" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/cmdk/node_modules/react-remove-scroll": { - "version": "2.5.5", - "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.5.5.tgz", - "integrity": "sha512-ImKhrzJJsyXJfBZ4bzu8Bwpka14c/fQt0k+cyFp/PBhTfyDnU5hjOtM4AG/0AMyy8oKzOTR0lDgJIM7pYXI0kw==", + "resolved": "https://registry.npmjs.org/cmdk/-/cmdk-1.0.4.tgz", + "integrity": "sha512-AnsjfHyHpQ/EFeAnG216WY7A5LiYCoZzCSygiLvfXC3H3LFGCprErteUcszaVluGOhuOTbJS3jWHrSDYPBBygg==", + "license": "MIT", "dependencies": { - "react-remove-scroll-bar": "^2.3.3", - "react-style-singleton": "^2.2.1", - "tslib": "^2.1.0", - "use-callback-ref": "^1.3.0", - "use-sidecar": "^1.1.2" - }, - "engines": { - "node": ">=10" + "@radix-ui/react-dialog": "^1.1.2", + "@radix-ui/react-id": "^1.1.0", + "@radix-ui/react-primitive": "^2.0.0", + "use-sync-external-store": "^1.2.2" }, "peerDependencies": { - "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } + "react": "^18 || ^19 || ^19.0.0-rc", + "react-dom": "^18 || ^19 || ^19.0.0-rc" } }, "node_modules/codepage": { @@ -8044,9 +7681,9 @@ } }, "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "license": "MIT", "dependencies": { "path-key": "^3.1.0", @@ -10921,9 +10558,9 @@ } }, "node_modules/i18next-http-backend": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/i18next-http-backend/-/i18next-http-backend-2.6.2.tgz", - "integrity": "sha512-Hp/kd8/VuoxIHmxsknJXjkTYYHzivAyAF15pzliKzk2TiXC25rZCEerb1pUFoxz4IVrG3fCvQSY51/Lu4ECV4A==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/i18next-http-backend/-/i18next-http-backend-3.0.1.tgz", + "integrity": "sha512-XT2lYSkbAtDE55c6m7CtKxxrsfuRQO3rUfHzj8ZyRtY9CkIX3aRGwXGTkUhpGWce+J8n7sfu3J0f2wTzo7Lw0A==", "license": "MIT", "dependencies": { "cross-fetch": "4.0.0" @@ -12742,12 +12379,6 @@ "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", "license": "MIT" }, - "node_modules/lodash-es": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", - "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==", - "license": "MIT" - }, "node_modules/lodash.castarray": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/lodash.castarray/-/lodash.castarray-4.4.0.tgz", @@ -14740,9 +14371,9 @@ "optional": true }, "node_modules/nanoid": { - "version": "3.3.7", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", - "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "version": "3.3.8", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz", + "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==", "funding": [ { "type": "github", @@ -16096,9 +15727,9 @@ } }, "node_modules/react-i18next": { - "version": "15.1.1", - "resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-15.1.1.tgz", - "integrity": "sha512-R/Vg9wIli2P3FfeI8o1eNJUJue5LWpFsQePCHdQDmX0Co3zkr6kdT8gAseb/yGeWbNz1Txc4bKDQuZYsC0kQfw==", + "version": "15.1.3", + "resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-15.1.3.tgz", + "integrity": "sha512-J11oA30FbM3NZegUZjn8ySK903z6PLBz/ZuBYyT1JMR0QPrW6PFXvl1WoUhortdGi9dM0m48/zJQlPskVZXgVw==", "license": "MIT", "dependencies": { "@babel/runtime": "^7.25.0", @@ -21305,4 +20936,4 @@ } } } -} +} \ No newline at end of file diff --git a/package.json b/package.json index d0666eda354..1ee17dc9691 100644 --- a/package.json +++ b/package.json @@ -75,7 +75,7 @@ "browserslist-useragent-regexp": "^4.1.3", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", - "cmdk": "^1.0.0", + "cmdk": "^1.0.4", "cross-env": "^7.0.3", "cypress": "^13.16.1", "dayjs": "^1.11.13", @@ -85,8 +85,7 @@ "hi-profiles": "^1.1.0", "i18next": "^23.16.4", "i18next-browser-languagedetector": "^8.0.0", - "i18next-http-backend": "^2.6.2", - "lodash-es": "^4.17.21", + "i18next-http-backend": "^3.0.1", "next-themes": "^0.4.4", "postcss-loader": "^8.1.1", "qrcode.react": "^4.1.0", @@ -95,7 +94,7 @@ "react-copy-to-clipboard": "^5.1.0", "react-dom": "18.3.1", "react-google-recaptcha": "^3.1.0", - "react-i18next": "^15.1.1", + "react-i18next": "^15.1.3", "react-infinite-scroll-component": "^6.1.0", "react-pdf": "^9.1.1", "react-webcam": "^7.2.0", @@ -115,7 +114,6 @@ "@types/events": "^3.0.3", "@types/google.maps": "^3.58.1", "@types/jsdom": "^21.1.7", - "@types/lodash-es": "^4.17.12", "@types/node": "^22.9.0", "@types/react": "^18.3.12", "@types/react-copy-to-clipboard": "^5.0.7", @@ -182,4 +180,4 @@ "node": ">=22.11.0" }, "packageManager": "npm@10.9.0" -} +} \ No newline at end of file diff --git a/public/locale/en.json b/public/locale/en.json index f86d78fa3aa..d73268df952 100644 --- a/public/locale/en.json +++ b/public/locale/en.json @@ -181,6 +181,14 @@ "ROUNDS_TYPE__NORMAL": "Brief Update", "ROUNDS_TYPE__TELEMEDICINE": "Tele-medicine Log", "ROUNDS_TYPE__VENTILATOR": "Detailed Update", + "SAMPLE_TEST_HISTORY__APPROVED": "Approved", + "SAMPLE_TEST_HISTORY__COMPLETED": "Completed", + "SAMPLE_TEST_HISTORY__RECEIVED_AT_LAB": "Received At Lab", + "SAMPLE_TEST_HISTORY__REQUEST_SUBMITTED": "Request Submitted", + "SAMPLE_TEST_RESULT__AWAITING": "Awaiting", + "SAMPLE_TEST_RESULT__INVALID": "Invalid", + "SAMPLE_TEST_RESULT__NEGATIVE": "Negative", + "SAMPLE_TEST_RESULT__POSITIVE": "Positive", "SLEEP__EXCESSIVE": "Excessive", "SLEEP__NO_SLEEP": "No sleep", "SLEEP__SATISFACTORY": "Satisfactory", diff --git a/src/Utils/Notifications.js b/src/Utils/Notifications.js index 5b3ecdf143c..08bc90443b5 100644 --- a/src/Utils/Notifications.js +++ b/src/Utils/Notifications.js @@ -1,6 +1,5 @@ import { Stack, alert, defaultModules } from "@pnotify/core"; import * as PNotifyMobile from "@pnotify/mobile"; -import { camelCase, startCase } from "lodash-es"; defaultModules.set(PNotifyMobile, {}); @@ -35,6 +34,25 @@ const notify = (text, type) => { }); }; +/** + * Formats input string to a more human readable format + * @param {string} key - The key to format + * @returns {string} The formatted key + * @example + * formatKey("patient_name") => "Patient Name" + */ +const formatKey = (key) => { + return key + .replace(/[^a-zA-Z0-9]+/g, " ") // Replace non-alphanumeric characters with a space + .trim() + .split(" ") + .map( + (word) => + word.charAt(0).toLocaleUpperCase() + word.slice(1).toLocaleLowerCase(), + ) // Capitalize the first letter of each word and lowercase the rest + .join(" "); +}; + const notifyError = (error) => { let errorMsg = ""; if (typeof error === "string" || !error) { @@ -44,7 +62,7 @@ const notifyError = (error) => { errorMsg = error.detail; } else { for (let [key, value] of Object.entries(error)) { - let keyName = startCase(camelCase(key)); + let keyName = formatKey(key); if (Array.isArray(value)) { const uniques = [...new Set(value)]; errorMsg += `${keyName} - ${uniques.splice(0, 5).join(", ")}`; diff --git a/src/Utils/utils.ts b/src/Utils/utils.ts index 3888406ad29..a7e6a0a78ec 100644 --- a/src/Utils/utils.ts +++ b/src/Utils/utils.ts @@ -544,3 +544,18 @@ export const fahrenheitToCelsius = (fahrenheit: number) => { export const keysOf = (obj: T) => { return Object.keys(obj) as (keyof T)[]; }; + +// Utility to check if a value is "empty" +export const isEmpty = (value: unknown) => { + return value === "" || value == undefined; +}; + +// equivalent to lodash omitBy +export function omitBy>( + obj: T, + predicate: (value: unknown) => boolean, +): Partial { + return Object.fromEntries( + Object.entries(obj).filter(([_, value]) => !predicate(value)), + ) as Partial; +} diff --git a/src/components/Common/ExcelFIleDragAndDrop.tsx b/src/components/Common/ExcelFIleDragAndDrop.tsx index 67c64f0f433..d868e03f2a4 100644 --- a/src/components/Common/ExcelFIleDragAndDrop.tsx +++ b/src/components/Common/ExcelFIleDragAndDrop.tsx @@ -1,4 +1,3 @@ -import { forIn } from "lodash-es"; import { useEffect, useRef, useState } from "react"; import { useTranslation } from "react-i18next"; import * as XLSX from "xlsx"; @@ -68,9 +67,9 @@ export default function ExcelFileDragAndDrop({ const data = XLSX.utils.sheet_to_json(worksheet, { defval: "" }); //converts the date to string data.forEach((row: any) => { - forIn(row, (value: any, key: string) => { - if (value instanceof Date) { - row[key] = value.toISOString().split("T")[0]; + Object.keys(row).forEach((key) => { + if (row[key] instanceof Date) { + row[key] = row[key].toISOString().split("T")[0]; } }); }); diff --git a/src/components/Common/Page.tsx b/src/components/Common/Page.tsx index 32e763c45b9..558d1432182 100644 --- a/src/components/Common/Page.tsx +++ b/src/components/Common/Page.tsx @@ -33,7 +33,7 @@ export default function Page(props: PageProps) { let padding = ""; if (!props.noImplicitPadding) { - if (!props.hideBack || props.componentRight) padding = "py-3 md:p-6"; + if (!props.hideBack || props.componentRight) padding = "py-3 md:px-6"; else padding = "px-6 py-5"; } diff --git a/src/components/Facility/Investigations/Reports/index.tsx b/src/components/Facility/Investigations/Reports/index.tsx index e711372da47..32f0abcb7bc 100644 --- a/src/components/Facility/Investigations/Reports/index.tsx +++ b/src/components/Facility/Investigations/Reports/index.tsx @@ -1,4 +1,3 @@ -import _ from "lodash"; import { useCallback, useReducer, useState } from "react"; import { useTranslation } from "react-i18next"; @@ -179,16 +178,16 @@ const InvestigationReports = ({ id }: any) => { ), ); - const investigationList = _.chain(data) - .flatMap((i) => i?.data?.results) - .compact() - .flatten() - .map((i) => ({ - ...i, - name: `${i.name} ${i.groups[0].name && " | " + i.groups[0].name} `, - })) - .unionBy("external_id") - .value(); + const investigationList = Array.from( + data + .flatMap((i) => i?.data?.results || []) + .map((i) => ({ + ...i, + name: `${i.name} ${i.groups[0].name ? " | " + i.groups[0].name : ""}`, + })) + .reduce((map, item) => map.set(item.external_id, item), new Map()) + .values(), + ); dispatch({ type: "set_investigations", payload: investigationList }); dispatch({ diff --git a/src/components/Facility/Investigations/Reports/utils.tsx b/src/components/Facility/Investigations/Reports/utils.tsx index 073be541600..564ce53ebe2 100644 --- a/src/components/Facility/Investigations/Reports/utils.tsx +++ b/src/components/Facility/Investigations/Reports/utils.tsx @@ -1,31 +1,70 @@ -import _ from "lodash"; -import { findIndex, memoize } from "lodash-es"; +import { + Investigation, + InvestigationResponse, +} from "@/components/Facility/Investigations/Reports/types"; -import { InvestigationResponse } from "@/components/Facility/Investigations/Reports/types"; +const memoize = any>(fn: T): T => { + const cache = new Map>(); + const MAX_CACHE_SIZE = 1000; + return ((...args: Parameters): ReturnType => { + const key = args + .map((arg) => + typeof arg === "object" + ? arg instanceof Date + ? arg.getTime().toString() + : JSON.stringify(Object.entries(arg).sort()) + : String(arg), + ) + .join("|"); + if (!cache.has(key)) { + if (cache.size >= MAX_CACHE_SIZE) { + const firstKey: any = cache.keys().next().value; + cache.delete(firstKey); + } + cache.set(key, fn(...args)); + } + return cache.get(key)!; + }) as T; +}; export const transformData = memoize((data: InvestigationResponse) => { - const sessions = _.chain(data) - .map((value: any) => { - return { - ...value.session_object, - facility_name: value.consultation_object?.facility_name, - facility_id: value.consultation_object?.facility, - }; - }) - .uniqBy("session_external_id") - .orderBy("session_created_date", "desc") - .value(); - const groupByInvestigation = _.chain(data) - .groupBy("investigation_object.external_id") - .values() - .value(); + const sessions = Array.from( + new Map( + data.map((value: any) => [ + value.session_object.session_external_id, + { + ...value.session_object, + facility_name: value.consultation_object?.facility_name, + facility_id: value.consultation_object?.facility, + }, + ]), + ).values(), + ).sort( + (a, b) => + new Date(b.session_created_date).getTime() - + new Date(a.session_created_date).getTime(), + ); + + const groupByInvestigation = Object.values( + data.reduce( + (acc, value: Investigation) => { + const key = value.investigation_object.external_id; + if (!acc[key]) acc[key] = []; + acc[key].push(value); + return acc; + }, + {} as { [key: string]: Investigation[] }, + ), + ); + + const sessionMap = new Map( + sessions.map((session, index) => [session.session_external_id, index]), + ); const reqData = groupByInvestigation.map((value: any) => { const sessionValues = Array.from({ length: sessions.length }); value.forEach((val: any) => { - const sessionIndex = findIndex(sessions, [ - "session_external_id", - val.session_object.session_external_id, - ]); + const sessionIndex = + sessionMap.get(val.session_object.session_external_id) ?? -1; if (sessionIndex > -1) { sessionValues[sessionIndex] = { min: val.investigation_object.min_value, @@ -58,6 +97,7 @@ export const transformData = memoize((data: InvestigationResponse) => { sessionValues, }; }); + return { sessions, data: reqData }; }); diff --git a/src/components/Facility/Investigations/ShowInvestigation.tsx b/src/components/Facility/Investigations/ShowInvestigation.tsx index 11e9b5cc0ad..05f2dd8eecf 100644 --- a/src/components/Facility/Investigations/ShowInvestigation.tsx +++ b/src/components/Facility/Investigations/ShowInvestigation.tsx @@ -1,5 +1,3 @@ -import _ from "lodash"; -import { set } from "lodash-es"; import { useCallback, useReducer } from "react"; import { useTranslation } from "react-i18next"; @@ -12,6 +10,8 @@ import routes from "@/Utils/request/api"; import request from "@/Utils/request/request"; import useQuery from "@/Utils/request/useQuery"; +// import { setNestedValueSafely } from "@/Utils/utils"; + const initialState = { changedFields: {}, initialValues: {}, @@ -92,8 +92,25 @@ export default function ShowInvestigation(props: ShowInvestigationProps) { }); const handleValueChange = (value: any, name: string) => { + const keys = name.split("."); + // Validate keys to prevent prototype pollution - coderabbit suggested + if ( + keys.some((key) => + ["__proto__", "constructor", "prototype"].includes(key), + ) + ) { + return; + } + const changedFields = { ...state.changedFields }; - set(changedFields, name, value); + let current = changedFields; + for (let i = 0; i < keys.length - 1; i++) { + const key = keys[i]; + if (!current[key]) current[key] = {}; + current = current[key]; + } + + current[keys[keys.length - 1]] = value; dispatch({ type: "set_changed_fields", changedFields }); }; @@ -151,15 +168,19 @@ export default function ShowInvestigation(props: ShowInvestigationProps) { }; const handleUpdateCancel = useCallback(() => { - const changedValues = _.chain(state.initialValues) - .map((val: any, _key: string) => ({ - id: val?.id, - initialValue: val?.notes || val?.value || null, - value: val?.value || null, - notes: val?.notes || null, - })) - .reduce((acc: any, cur: any) => ({ ...acc, [cur.id]: cur }), {}) - .value(); + const changedValues = Object.keys(state.initialValues).reduce( + (acc: any, key: any) => { + const val = state.initialValues[key]; + acc[key] = { + id: val?.id, + initialValue: val?.notes || val?.value || null, + value: val?.value || null, + notes: val?.notes || null, + }; + return acc; + }, + {}, + ); dispatch({ type: "set_changed_fields", changedFields: changedValues }); }, [state.initialValues]); diff --git a/src/components/Facility/Investigations/Table.tsx b/src/components/Facility/Investigations/Table.tsx index 3a267279eb7..49dd924480b 100644 --- a/src/components/Facility/Investigations/Table.tsx +++ b/src/components/Facility/Investigations/Table.tsx @@ -1,4 +1,3 @@ -import { set } from "lodash-es"; import { useState } from "react"; import { SelectFormField } from "@/components/Form/FormFields/SelectFormField"; @@ -60,7 +59,24 @@ export const TestTable = ({ title, data, state, dispatch }: any) => { const handleValueChange = (value: any, name: string) => { const form = { ...state }; - set(form, name, value); + const keys = name.split("."); + + // Validate keys to prevent prototype pollution - coderabbit suggested + if ( + keys.some((key) => + ["__proto__", "constructor", "prototype"].includes(key), + ) + ) { + return; + } + + let current = form; + for (let i = 0; i < keys.length - 1; i++) { + const key = keys[i]; + if (!current[key]) current[key] = {}; + current = current[key]; + } + current[keys[keys.length - 1]] = value; dispatch({ type: "set_form", form }); }; diff --git a/src/components/Form/AutoCompleteAsync.tsx b/src/components/Form/AutoCompleteAsync.tsx index b0cb9208d73..48c36258765 100644 --- a/src/components/Form/AutoCompleteAsync.tsx +++ b/src/components/Form/AutoCompleteAsync.tsx @@ -5,8 +5,7 @@ import { ComboboxOption, ComboboxOptions, } from "@headlessui/react"; -import { debounce } from "lodash-es"; -import { useEffect, useMemo, useState } from "react"; +import { useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; import CareIcon from "@/CAREUI/icons/CareIcon"; @@ -17,6 +16,8 @@ import { dropdownOptionClassNames, } from "@/components/Form/MultiSelectMenuV2"; +import useDebounce from "@/hooks/useDebounce"; + import { classNames } from "@/Utils/utils"; interface Props { @@ -69,27 +70,29 @@ const AutoCompleteAsync = (props: Props) => { const hasSelection = (!multiple && selected) || (multiple && selected?.length > 0); - const fetchDataAsync = useMemo( - () => - debounce(async (query: string) => { - setLoading(true); - const data = ((await fetchData(query)) || [])?.filter((d: any) => - filter ? filter(d) : true, - ); + const fetchDataDebounced = useDebounce(async (searchQuery: string) => { + setLoading(true); + try { + const fetchedData = (await fetchData(searchQuery)) || []; + const filteredData = filter + ? fetchedData.filter((item: any) => filter(item)) + : fetchedData; - if (showNOptions !== undefined) { - setData(data.slice(0, showNOptions)); - } else { - setData(data); - } - setLoading(false); - }, debounceTime), - [fetchData, showNOptions, debounceTime], - ); + setData( + showNOptions !== undefined + ? filteredData.slice(0, showNOptions) + : filteredData, + ); + } catch (error) { + console.error("Error fetching data:", error); + } finally { + setLoading(false); + } + }, debounceTime); useEffect(() => { - fetchDataAsync(query); - }, [query, fetchDataAsync]); + fetchDataDebounced(query); + }, [query, fetchDataDebounced]); return (
diff --git a/src/components/Form/Form.tsx b/src/components/Form/Form.tsx index e31467b6c2a..63b8c3614e5 100644 --- a/src/components/Form/Form.tsx +++ b/src/components/Form/Form.tsx @@ -1,4 +1,3 @@ -import { isEmpty, omitBy } from "lodash-es"; import { useEffect, useMemo, useState } from "react"; import { Cancel, Submit } from "@/components/Common/ButtonV2"; @@ -17,7 +16,7 @@ import { import { DraftSection, useAutoSaveReducer } from "@/Utils/AutoSave"; import * as Notification from "@/Utils/Notifications"; -import { classNames } from "@/Utils/utils"; +import { classNames, isEmpty, omitBy } from "@/Utils/utils"; type Props = { className?: string; @@ -59,6 +58,7 @@ const Form = ({ if (validate) { const errors = omitBy(validate(state.form), isEmpty) as FormErrors; + if (Object.keys(errors).length) { dispatch({ type: "set_errors", errors }); diff --git a/src/components/Patient/DiagnosesFilter.tsx b/src/components/Patient/DiagnosesFilter.tsx index c8fc2bc1a44..912a10cfde2 100644 --- a/src/components/Patient/DiagnosesFilter.tsx +++ b/src/components/Patient/DiagnosesFilter.tsx @@ -1,4 +1,3 @@ -import { debounce } from "lodash-es"; import { useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; @@ -6,6 +5,8 @@ import { ICD11DiagnosisModel } from "@/components/Diagnosis/types"; import { getDiagnosesByIds } from "@/components/Diagnosis/utils"; import AutocompleteMultiSelectFormField from "@/components/Form/FormFields/AutocompleteMultiselect"; +import useDebounce from "@/hooks/useDebounce"; + import { Error } from "@/Utils/Notifications"; import routes from "@/Utils/request/api"; import useQuery from "@/Utils/request/useQuery"; @@ -34,6 +35,7 @@ interface Props { value?: string; onChange: (event: { name: DiagnosesFilterKey; value: string }) => void; } + export default function DiagnosesFilter(props: Props) { const { t } = useTranslation(); const [diagnoses, setDiagnoses] = useState([]); @@ -42,6 +44,11 @@ export default function DiagnosesFilter(props: Props) { prefetch: false, }); + const handleQuery = useDebounce( + (query: string) => refetch({ query: { query } }), + 300, + ); + useEffect(() => { if (res?.status === 500) { Error({ msg: "ICD-11 Diagnosis functionality is facing issues." }); @@ -88,7 +95,7 @@ export default function DiagnosesFilter(props: Props) { options={mergeQueryOptions(diagnoses, data ?? [], (obj) => obj.id)} optionLabel={(option) => option.label} optionValue={(option) => option} - onQuery={debounce((query: string) => refetch({ query: { query } }), 300)} + onQuery={handleQuery} isLoading={loading} /> ); diff --git a/src/components/Patient/ManagePatients.tsx b/src/components/Patient/ManagePatients.tsx index 6fba4d6e734..65fc0e73933 100644 --- a/src/components/Patient/ManagePatients.tsx +++ b/src/components/Patient/ManagePatients.tsx @@ -749,14 +749,6 @@ export const PatientManager = () => { permittedFacilities?.count === 1 ? permittedFacilities.results[0] : null; const searchOptions = [ - { - key: "phone_number", - label: "Phone Number", - type: "phone" as const, - placeholder: "Search_by_phone_number", - value: qParams.phone_number || "", - shortcutKey: "p", - }, { key: "name", label: "Name", @@ -773,6 +765,14 @@ export const PatientManager = () => { value: qParams.patient_no || "", shortcutKey: "u", }, + { + key: "phone_number", + label: "Phone Number", + type: "phone" as const, + placeholder: "Search_by_phone_number", + value: qParams.phone_number || "", + shortcutKey: "p", + }, { key: "emergency_contact_number", label: "Emergency Contact Phone Number", diff --git a/src/components/Patient/PatientRegister.tsx b/src/components/Patient/PatientRegister.tsx index 5828517adf9..a7098c24264 100644 --- a/src/components/Patient/PatientRegister.tsx +++ b/src/components/Patient/PatientRegister.tsx @@ -1,6 +1,4 @@ import careConfig from "@careConfig"; -import { startCase, toLower } from "lodash-es"; -import { debounce } from "lodash-es"; import { navigate } from "raviger"; import { useCallback, useEffect, useReducer, useRef, useState } from "react"; import { useTranslation } from "react-i18next"; @@ -56,6 +54,7 @@ import { UserModel } from "@/components/Users/models"; import useAppHistory from "@/hooks/useAppHistory"; import useAuthUser from "@/hooks/useAuthUser"; +import useDebounce from "@/hooks/useDebounce"; import { BLOOD_GROUPS, @@ -656,7 +655,7 @@ export const PatientRegister = (props: PatientRegisterProps) => { ? formData.last_vaccinated_date : null : null, - name: startCase(toLower(formData.name)), + name: formData.name, pincode: formData.pincode ? formData.pincode : undefined, gender: Number(formData.gender), nationality: formData.nationality, @@ -776,7 +775,7 @@ export const PatientRegister = (props: PatientRegisterProps) => { }); }; - const duplicateCheck = debounce(async (phoneNo: string) => { + const duplicateCheck = useDebounce(async (phoneNo: string) => { if ( phoneNo && PhoneNumberValidator()(parsePhoneNumber(phoneNo) ?? "") === undefined @@ -800,6 +799,11 @@ export const PatientRegister = (props: PatientRegisterProps) => { }); } } + } else { + setStatusDialog({ + show: false, + patientList: [], + }); } }, 300); @@ -1022,6 +1026,7 @@ export const PatientRegister = (props: PatientRegisterProps) => { {...field("name")} type="text" label={"Name"} + autoCapitalize="words" />
diff --git a/src/components/Patient/SampleDetails.tsx b/src/components/Patient/SampleDetails.tsx index abf099a6e7d..9a779446d54 100644 --- a/src/components/Patient/SampleDetails.tsx +++ b/src/components/Patient/SampleDetails.tsx @@ -1,10 +1,10 @@ -import { camelCase, capitalize, startCase } from "lodash-es"; -import { navigate } from "raviger"; +import { Link, navigate } from "raviger"; import { useTranslation } from "react-i18next"; -import Card from "@/CAREUI/display/Card"; +import { Badge } from "@/components/ui/badge"; +import { Button } from "@/components/ui/button"; +import { Card, CardContent, CardHeader } from "@/components/ui/card"; -import ButtonV2 from "@/components/Common/ButtonV2"; import Loading from "@/components/Common/Loading"; import Page from "@/components/Common/Page"; import { FileUpload } from "@/components/Files/FileUpload"; @@ -270,11 +270,11 @@ export const SampleDetails = ({ id }: DetailRoute) => { {t("status")}:{" "} {" "} - {startCase(camelCase(flow.status))} + {t(`SAMPLE_TEST_HISTORY__${flow.status}`) || "Unknown"}
{t("label")}:{" "} - {capitalize(flow.notes)} + {flow.notes}
@@ -305,169 +305,230 @@ export const SampleDetails = ({ id }: DetailRoute) => { options={ sampleDetails?.patient && (
- - {t("icmr_specimen_referral_form")} - +
) } > - -
-
- - {t("status")}:{" "} - - {sampleDetails?.status} -
-
- - {t("result")}:{" "} - - {sampleDetails?.result} -
-
- - {t("patient")}:{" "} - - {sampleDetails?.patient_name} -
- {sampleDetails?.facility_object && ( -
- - {t("facility")}:{" "} - - {sampleDetails?.facility_object.name} + + +
+
+
+ {t("status")}:{" "} +
+ + {sampleDetails?.status} + +
+
+
+ {t("result")}:{" "} +
+ + {sampleDetails?.result} +
- )} -
- - {t("tested_on")}:{" "} - - {sampleDetails?.date_of_result - ? formatDateTime(sampleDetails.date_of_result) - : "-"} -
-
- - {t("result_on")}:{" "} - - {sampleDetails?.date_of_result - ? formatDateTime(sampleDetails.date_of_result) - : "-"}
- {sampleDetails?.fast_track && ( -
- - {t("fast_track_testing_reason")}:{" "} - - {sampleDetails.fast_track} + + +
+
+
+ {t("patient")}: +
+
+ {sampleDetails?.patient_name || "-"} +
- )} - {sampleDetails?.doctor_name && ( -
- - {t("doctors_name")}:{" "} - - - {startCase(camelCase(sampleDetails.doctor_name))} - + {sampleDetails?.facility_object && ( +
+
+ {t("facility")}:{" "} +
+
+ {sampleDetails?.facility_object.name} +
+
+ )} +
+
+ {t("tested_on")}:{" "} +
+
+ {sampleDetails?.date_of_result + ? formatDateTime(sampleDetails.date_of_result) + : "-"} +
- )} - {sampleDetails?.diagnosis && ( -
- - {t("diagnosis")}:{" "} - - {sampleDetails.diagnosis} +
+
+ {t("result_on")}:{" "} +
+
+ {sampleDetails?.date_of_result + ? formatDateTime(sampleDetails.date_of_result) + : "-"} +
- )} - {sampleDetails?.diff_diagnosis && ( -
- - {t("differential_diagnosis")}:{" "} - - {sampleDetails?.diff_diagnosis} +
+ + {sampleDetails?.doctor_name && ( +
+
+ {t("doctors_name")}: +
+
+ {sampleDetails.doctor_name} +
)} - {sampleDetails?.etiology_identified && ( -
- - {t("etiology_identified")}:{" "} - - - {sampleDetails.etiology_identified} - +
+
+ {sampleDetails?.fast_track && ( +
+
+ {t("fast_track_testing_reason")}:{" "} +
+ {sampleDetails.fast_track} +
+ )} + {sampleDetails?.diagnosis && ( +
+
{t("diagnosis")}:
+ + {" "} + {sampleDetails.diagnosis} + +
+ )} + {sampleDetails?.diff_diagnosis && ( +
+
+ {t("differential_diagnosis")}:{" "} +
+ + {" "} + {sampleDetails?.diff_diagnosis} + +
+ )} + {sampleDetails?.etiology_identified && ( +
+
+ {t("etiology_identified")}:{" "} +
+ + {" "} + {sampleDetails.etiology_identified} + +
+ )} +
+
+ {t("is_atypical_presentation")} +
+ + {" "} + {yesOrNoBadge(sampleDetails?.is_atypical_presentation)} +
- )} -
- - {t("is_atypical_presentation")}{" "} - - {yesOrNoBadge(sampleDetails?.is_atypical_presentation)} -
-
- - {t("is_unusual_course")}{" "} - - {yesOrNoBadge(sampleDetails?.is_unusual_course)} -
- {sampleDetails?.atypical_presentation && ( -
- - {t("atypical_presentation_details")}:{" "} - - {sampleDetails.atypical_presentation} +
+
+ {t("is_unusual_course")} +
+ + {" "} + {yesOrNoBadge(sampleDetails?.is_unusual_course)} +
- )} -
- {t("sari")} - {yesOrNoBadge(sampleDetails?.has_sari)} -
-
- {t("ari")} - {yesOrNoBadge(sampleDetails?.has_ari)} -
-
- - {t("contact_with_confirmed_carrier")}{" "} - - {yesOrNoBadge(sampleDetails?.patient_has_confirmed_contact)} -
-
- - {t("contact_with_suspected_carrier")}{" "} - - {yesOrNoBadge(sampleDetails?.patient_has_suspected_contact)} -
- {sampleDetails?.patient_travel_history && - sampleDetails.patient_travel_history.length !== 0 && ( -
- - {t("countries_travelled")}:{" "} - - {sampleDetails.patient_travel_history} + {sampleDetails?.atypical_presentation && ( +
+
+ {t("atypical_presentation_details")}:{" "} +
+ + {" "} + {sampleDetails.atypical_presentation} +
)} +
+
{t("sari")}
+ + {" "} + {yesOrNoBadge(sampleDetails?.has_sari)} + +
+
+
{t("ari")}
+ + {" "} + {yesOrNoBadge(sampleDetails?.has_ari)} + +
+
+
+ {t("contact_with_confirmed_carrier")}{" "} +
+ + {" "} + {yesOrNoBadge(sampleDetails?.patient_has_confirmed_contact)} + +
+
+
+ {t("contact_with_suspected_carrier")}{" "} +
+ + {" "} + {yesOrNoBadge(sampleDetails?.patient_has_suspected_contact)} + +
+ {sampleDetails?.patient_travel_history && + sampleDetails.patient_travel_history.length !== 0 && ( +
+
+ {t("countries_travelled")}:{" "} +
+ + {" "} + {sampleDetails.patient_travel_history} + +
+ )} +
+
{sampleDetails?.sample_type && ( -
- +
+
{t("sample_type")}:{" "} +
+ + {sampleDetails.sample_type} - {startCase(camelCase(sampleDetails.sample_type))}
)} {sampleDetails?.sample_type === "OTHER TYPE" && ( -
- +
+
{t("sample_type_description")}:{" "} - - {sampleDetails?.sample_type_other} +
+
+ {sampleDetails?.sample_type_other} +
)} -
+
diff --git a/src/components/Patient/SampleTestCard.tsx b/src/components/Patient/SampleTestCard.tsx index c22155c494e..f1d59980c23 100644 --- a/src/components/Patient/SampleTestCard.tsx +++ b/src/components/Patient/SampleTestCard.tsx @@ -1,6 +1,6 @@ -import { camelCase, startCase } from "lodash-es"; import { navigate } from "raviger"; import { useState } from "react"; +import { useTranslation } from "react-i18next"; import ButtonV2 from "@/components/Common/ButtonV2"; import RelativeDateUserMention from "@/components/Common/RelativeDateUserMention"; @@ -24,6 +24,7 @@ interface SampleDetailsProps { } export const SampleTestCard = (props: SampleDetailsProps) => { + const { t } = useTranslation(); const { itemData, handleApproval, facilityId, patientId, refetch } = props; const [statusDialog, setStatusDialog] = useState<{ @@ -103,9 +104,9 @@ export const SampleTestCard = (props: SampleDetailsProps) => {
- {startCase(camelCase(itemData.status))} + {t(`SAMPLE_TEST_HISTORY__${itemData.status}`) || "Unknown"}
@@ -144,9 +145,9 @@ export const SampleTestCard = (props: SampleDetailsProps) => {
- {startCase(camelCase(itemData.result))} + {t(`SAMPLE_TEST_RESULT__${itemData.result}`) || "Unknown"}
diff --git a/src/components/Resource/ResourceBoard.tsx b/src/components/Resource/ResourceBoard.tsx index f76604ff9f6..1f8ccafcb5d 100644 --- a/src/components/Resource/ResourceBoard.tsx +++ b/src/components/Resource/ResourceBoard.tsx @@ -5,7 +5,8 @@ import { useTranslation } from "react-i18next"; import CareIcon from "@/CAREUI/icons/CareIcon"; import { AdvancedFilterButton } from "@/CAREUI/interactive/FiltersSlideover"; -import ButtonV2 from "@/components/Common/ButtonV2"; +import { Button } from "@/components/ui/button"; + import { ExportButton } from "@/components/Common/Export"; import Loading from "@/components/Common/Loading"; import PageTitle from "@/components/Common/PageTitle"; @@ -92,10 +93,10 @@ export default function BoardView() { currentTab={boardFilter !== ACTIVE ? 1 : 0} />
- - + advancedFilter.setShow(true)} /> diff --git a/src/components/Resource/ResourceList.tsx b/src/components/Resource/ResourceList.tsx index 891c2f3378b..3bca8377a6b 100644 --- a/src/components/Resource/ResourceList.tsx +++ b/src/components/Resource/ResourceList.tsx @@ -5,7 +5,8 @@ import Chip from "@/CAREUI/display/Chip"; import CareIcon from "@/CAREUI/icons/CareIcon"; import { AdvancedFilterButton } from "@/CAREUI/interactive/FiltersSlideover"; -import ButtonV2 from "@/components/Common/ButtonV2"; +import { Button } from "@/components/ui/button"; + import { ExportButton } from "@/components/Common/Export"; import Loading from "@/components/Common/Loading"; import Page from "@/components/Common/Page"; @@ -34,8 +35,10 @@ export default function ListView() { const { t } = useTranslation(); - const onBoardViewBtnClick = () => + const onBoardViewBtnClick = () => { navigate("/resource/board", { query: qParams }); + localStorage.setItem("defaultResourceView", "board"); + }; const appliedFilters = formatFilter(qParams); const { loading, data, refetch } = useQuery(routes.listResourceRequests, { @@ -216,10 +219,10 @@ export default function ListView() {
- - + advancedFilter.setShow(true)} /> diff --git a/src/components/Shifting/ShiftingBoard.tsx b/src/components/Shifting/ShiftingBoard.tsx index 8486c8dd4d0..cd9f4300fd4 100644 --- a/src/components/Shifting/ShiftingBoard.tsx +++ b/src/components/Shifting/ShiftingBoard.tsx @@ -6,7 +6,8 @@ import { useTranslation } from "react-i18next"; import CareIcon from "@/CAREUI/icons/CareIcon"; import { AdvancedFilterButton } from "@/CAREUI/interactive/FiltersSlideover"; -import ButtonV2 from "@/components/Common/ButtonV2"; +import { Button } from "@/components/ui/button"; + import ConfirmDialog from "@/components/Common/ConfirmDialog"; import { ExportButton } from "@/components/Common/Export"; import Loading from "@/components/Common/Loading"; @@ -75,6 +76,10 @@ export default function BoardView() { const [boardFilter, setBoardFilter] = useState(activeBoards); const { t } = useTranslation(); + const onListViewBtnClick = () => { + navigate("/shifting/list", { query: qParams }); + localStorage.setItem("defaultShiftView", "list"); + }; return (
@@ -119,13 +124,10 @@ export default function BoardView() { />
- navigate("/shifting/list", { query: qParams })} - > - + advancedFilter.setShow(true)} /> diff --git a/src/components/Shifting/ShiftingList.tsx b/src/components/Shifting/ShiftingList.tsx index 7161ea29441..f9727bdd819 100644 --- a/src/components/Shifting/ShiftingList.tsx +++ b/src/components/Shifting/ShiftingList.tsx @@ -4,7 +4,8 @@ import { useTranslation } from "react-i18next"; import CareIcon from "@/CAREUI/icons/CareIcon"; import { AdvancedFilterButton } from "@/CAREUI/interactive/FiltersSlideover"; -import ButtonV2 from "@/components/Common/ButtonV2"; +import { Button } from "@/components/ui/button"; + import { ExportButton } from "@/components/Common/Export"; import Loading from "@/components/Common/Loading"; import Page from "@/components/Common/Page"; @@ -32,6 +33,10 @@ export default function ListView() { } = useFilters({ cacheBlacklist: ["patient_name"] }); const { t } = useTranslation(); + const onBoardViewBtnClick = () => { + navigate("/shifting/board", { query: qParams }); + localStorage.setItem("defaultShiftView", "board"); + }; const { data: shiftData, loading, @@ -73,13 +78,10 @@ export default function ListView() {
- navigate("/shifting/board", { query: qParams })} - > - + advancedFilter.setShow(true)} /> diff --git a/src/components/ui/badge.tsx b/src/components/ui/badge.tsx new file mode 100644 index 00000000000..99b8f3bbda4 --- /dev/null +++ b/src/components/ui/badge.tsx @@ -0,0 +1,43 @@ +import { type VariantProps, cva } from "class-variance-authority"; +import * as React from "react"; + +import { cn } from "@/lib/utils"; + +const badgeVariants = cva( + "inline-flex items-center rounded-md border border-gray-200 px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-gray-950 focus:ring-offset-2 dark:border-gray-800 dark:focus:ring-gray-300", + { + variants: { + variant: { + default: + "border-transparent bg-gray-900 text-gray-50 shadow hover:bg-gray-900/80 dark:bg-gray-50 dark:text-gray-900 dark:hover:bg-gray-50/80", + secondary: + "border-transparent bg-gray-100 text-gray-900 hover:bg-gray-100/80 dark:bg-gray-800 dark:text-gray-50 dark:hover:bg-gray-800/80", + destructive: + "border-transparent bg-red-500 text-gray-50 shadow hover:bg-red-500/80 dark:bg-red-900 dark:text-gray-50 dark:hover:bg-red-900/80", + warning: + "border-transparent bg-yellow-400 text-gray-900 shadow hover:bg-yellow-500 dark:bg-yellow-400 dark:text-gray-900 dark:hover:bg-yellow-500", + outline: "text-gray-950 dark:text-gray-50", + }, + }, + defaultVariants: { + variant: "default", + }, + }, +); + +export interface BadgeProps + extends React.HTMLAttributes, + VariantProps {} + +function Badge({ className, variant, ...props }: BadgeProps) { + return ( +
+ ); +} + +export { Badge, badgeVariants }; diff --git a/src/hooks/useDebounce.ts b/src/hooks/useDebounce.ts new file mode 100644 index 00000000000..66ee398f962 --- /dev/null +++ b/src/hooks/useDebounce.ts @@ -0,0 +1,29 @@ +import { useEffect, useRef } from "react"; + +export default function useDebounce( + callback: (...args: T) => void, + delay: number, +) { + const callbackRef = useRef(callback); + const timeoutRef = useRef | null>(null); + + useEffect(() => { + callbackRef.current = callback; + }, [callback]); + + useEffect(() => { + return () => { + if (timeoutRef.current) clearTimeout(timeoutRef.current); + }; + }, []); + + const debouncedCallback = (...args: T) => { + if (timeoutRef.current) { + clearTimeout(timeoutRef.current); + } + timeoutRef.current = setTimeout(() => { + callbackRef.current(...args); + }, delay); + }; + return debouncedCallback; +}