diff --git a/cypress/e2e/assets_spec/assets_manage.cy.ts b/cypress/e2e/assets_spec/assets_manage.cy.ts index f27fd302a0c..34c554b374e 100644 --- a/cypress/e2e/assets_spec/assets_manage.cy.ts +++ b/cypress/e2e/assets_spec/assets_manage.cy.ts @@ -1,10 +1,20 @@ import { afterEach, before, beforeEach, cy, describe, it } from "local-cypress"; import { AssetPage } from "../../pageobject/Asset/AssetCreation"; import LoginPage from "../../pageobject/Login/LoginPage"; +import { AssetSearchPage } from "../../pageobject/Asset/AssetSearch"; +import FacilityPage from "../../pageobject/Facility/FacilityCreation"; +import { AssetFilters } from "../../pageobject/Asset/AssetFilters"; describe("Asset", () => { const assetPage = new AssetPage(); const loginPage = new LoginPage(); + const facilityPage = new FacilityPage(); + const assetSearchPage = new AssetSearchPage(); + const assetFilters = new AssetFilters(); + const fillFacilityName = "Dummy Facility 1"; + const assetname = "Dummy Camera"; + const locationName = "Dummy Location 1"; + const initiallocationName = "Camera Location"; before(() => { loginPage.loginAsDisctrictAdmin(); @@ -16,6 +26,57 @@ describe("Asset", () => { cy.awaitUrl("/assets"); }); + it("Create & Edit a service history and verify reflection", () => { + assetSearchPage.typeSearchKeyword(assetname); + assetSearchPage.pressEnter(); + assetSearchPage.verifyBadgeContent(assetname); + assetSearchPage.clickAssetByName(assetname); + assetPage.clickupdatedetailbutton(); + assetPage.scrollintonotes(); + assetPage.enterAssetNotes("Dummy Notes"); + assetPage.enterAssetservicedate("01092023"); + assetPage.clickassetupdatebutton(); + assetPage.scrollintoservicehistory(); + assetPage.clickedithistorybutton(); + assetPage.scrollintonotes(); + assetPage.enterAssetNotes("Dummy Notes Editted"); + assetPage.clickassetupdatebutton(); + assetPage.scrollintoservicehistory(); + assetPage.viewassetservicehistorybutton(); + assetPage.openassetservicehistory(); + assetPage.verifyassetupdateservicehistory(); + assetPage.viewassetservicehistorybutton(); + }); + + it("Create a asset transaction and verify history", () => { + assetSearchPage.typeSearchKeyword(assetname); + assetSearchPage.pressEnter(); + assetSearchPage.verifyBadgeContent(assetname); + assetSearchPage.clickAssetByName(assetname); + assetPage.clickupdatedetailbutton(); + assetPage.clickassetlocation(locationName); + assetPage.clickUpdateAsset(); + assetPage.verifyassetlocation(locationName); + assetPage.verifytransactionStatus(initiallocationName, locationName); + }); + + it("Verify Facility Asset Page Redirection", () => { + cy.visit("/facility"); + assetSearchPage.typeSearchKeyword(fillFacilityName); + assetSearchPage.pressEnter(); + facilityPage.verifyFacilityBadgeContent(fillFacilityName); + facilityPage.visitAlreadyCreatedFacility(); + facilityPage.clickManageFacilityDropdown(); + facilityPage.clickCreateAssetFacilityOption(); + facilityPage.verifyfacilitycreateassetredirection(); + facilityPage.verifyassetfacilitybackredirection(); + facilityPage.clickManageFacilityDropdown(); + facilityPage.clickviewAssetFacilityOption(); + facilityPage.verifyfacilityviewassetredirection(); + assetFilters.assertFacilityText(fillFacilityName); + facilityPage.verifyassetfacilitybackredirection(); + }); + it("Delete an Asset", () => { assetPage.openCreatedAsset(); assetPage.interceptDeleteAssetApi(); @@ -23,6 +84,23 @@ describe("Asset", () => { assetPage.verifyDeleteStatus(); }); + it("Verify Facility Asset Page Redirection", () => { + cy.visit("/facility"); + assetSearchPage.typeSearchKeyword(fillFacilityName); + assetSearchPage.pressEnter(); + facilityPage.verifyFacilityBadgeContent(fillFacilityName); + facilityPage.visitAlreadyCreatedFacility(); + facilityPage.clickManageFacilityDropdown(); + facilityPage.clickCreateAssetFacilityOption(); + facilityPage.verifyfacilitycreateassetredirection(); + facilityPage.verifyassetfacilitybackredirection(); + facilityPage.clickManageFacilityDropdown(); + facilityPage.clickviewAssetFacilityOption(); + facilityPage.verifyfacilityviewassetredirection(); + assetFilters.assertFacilityText(fillFacilityName); + facilityPage.verifyassetfacilitybackredirection(); + }); + afterEach(() => { cy.saveLocalStorage(); }); diff --git a/cypress/e2e/patient_spec/patient_crud.cy.ts b/cypress/e2e/patient_spec/patient_crud.cy.ts index b55732fa440..fcfd97039f7 100644 --- a/cypress/e2e/patient_spec/patient_crud.cy.ts +++ b/cypress/e2e/patient_spec/patient_crud.cy.ts @@ -33,6 +33,7 @@ describe("Patient Creation with consultation", () => { it("Create a new patient with no consultation", () => { patientPage.createPatient(); patientPage.selectFacility("dummy facility"); + patientPage.patientformvisibility(); patientPage.enterPatientDetails( phone_number, emergency_phone_number, @@ -69,6 +70,7 @@ describe("Patient Creation with consultation", () => { patientPage.interceptFacilities(); patientPage.visitUpdatePatientUrl(); patientPage.verifyStatusCode(); + patientPage.patientformvisibility(); updatePatientPage.enterPatientDetails( "Test E2E User Edited", "O+", @@ -113,6 +115,7 @@ describe("Patient Creation with consultation", () => { patientConsultationPage.selectSymptoms("ASYMPTOMATIC"); patientConsultationPage.enterConsultationDetails( + "Stable", "Examination details and Clinical conditions", "70", "170", diff --git a/cypress/pageobject/Asset/AssetCreation.ts b/cypress/pageobject/Asset/AssetCreation.ts index b939c31405f..45356a1a4e7 100644 --- a/cypress/pageobject/Asset/AssetCreation.ts +++ b/cypress/pageobject/Asset/AssetCreation.ts @@ -276,4 +276,78 @@ export class AssetPage { cy.get("#submit").contains("Import").click(); cy.wait("@importAsset").its("response.statusCode").should("eq", 201); } + + clickupdatedetailbutton() { + cy.get("[data-testid=asset-update-button]").click(); + } + + scrollintonotes() { + cy.get("#notes").scrollIntoView(); + } + + enterAssetNotes(text) { + cy.get("#notes").click().clear(); + cy.get("#notes").click().type(text); + } + + enterAssetservicedate(text) { + cy.get("input[name='last_serviced_on']").click(); + cy.get("#date-input").click().type(text); + } + + clickassetupdatebutton() { + cy.get("#submit").click(); + } + + viewassetservicehistorybutton() { + cy.get("#view-service-history").should("be.visible"); + } + + openassetservicehistory() { + cy.get("#view-service-history").click(); + cy.get("#view-asset-edit-history").first().click(); + } + + verifyassetupdateservicehistory() { + cy.get("#edit-history-asset-servicedon").should("have.text", "01/09/2023"); + cy.get("#edit-history-asset-note").should( + "have.text", + "Dummy Notes Editted" + ); + cy.get("#view-history-back-button").contains("Back").click(); + cy.get("#view-history-back-button").contains("Close").click(); + } + + scrollintoservicehistory() { + cy.get("#service-history").scrollIntoView(); + } + + clickedithistorybutton() { + cy.get("#edit-service-history").click(); + } + + verifytransactionStatus(initiallocationName: string, locationName: string) { + cy.get("#transaction-history").scrollIntoView(); + cy.get("#transaction-history table tbody tr:first-child td:eq(0)").should( + "contain", + initiallocationName + ); + cy.get("#transaction-history table tbody tr:first-child td:eq(1)").should( + "contain", + locationName + ); + } + + verifyassetlocation(locationName: string) { + cy.get("#asset-current-location").should("contain", locationName); + } + + clickassetlocation(locationName: string) { + cy.get("#clear-button").click(); + cy.get("[data-testid=asset-location-input] button").click(); + cy.get("[data-testid=asset-location-input] button") + .click() + .type(locationName); + cy.get("[role='option']").contains(locationName).click(); + } } diff --git a/cypress/pageobject/Facility/FacilityCreation.ts b/cypress/pageobject/Facility/FacilityCreation.ts index ef4e65781e0..42ec6d8fd7c 100644 --- a/cypress/pageobject/Facility/FacilityCreation.ts +++ b/cypress/pageobject/Facility/FacilityCreation.ts @@ -120,6 +120,14 @@ class FacilityPage { cy.get("#configure-facility").contains("Configure Facility").click(); } + clickCreateAssetFacilityOption() { + cy.get("#create-assets").contains("Create Asset").click(); + } + + clickviewAssetFacilityOption() { + cy.get("#view-assets").contains("View Assets").click(); + } + clickInventoryManagementOption() { cy.get("#inventory-management", { timeout: 10000 }).should("be.visible"); cy.get("#inventory-management").click(); @@ -175,6 +183,29 @@ class FacilityPage { cy.wait("@getFacilities").its("response.statusCode").should("eq", 200); } + verifyFacilityBadgeContent(expectedText: string) { + cy.get("[data-testid='Facility/District Name']").should( + "contain", + expectedText + ); + } + + verifyfacilitycreateassetredirection() { + cy.url().should("include", "/assets/new"); + } + + verifyassetfacilitybackredirection() { + cy.intercept("GET", "**/api/v1/facility/**").as("getManagePage"); + cy.go("back"); + cy.wait("@getManagePage").its("response.statusCode").should("eq", 200); + cy.get("#manage-facility-dropdown").scrollIntoView(); + cy.get("#manage-facility-dropdown").should("exist"); + } + + verifyfacilityviewassetredirection() { + cy.url().should("include", "/assets?facility="); + } + clickManageInventory() { cy.contains("Manage Inventory").click(); } diff --git a/cypress/pageobject/Patient/PatientConsultation.ts b/cypress/pageobject/Patient/PatientConsultation.ts index c0ba0c260d8..6937559d548 100644 --- a/cypress/pageobject/Patient/PatientConsultation.ts +++ b/cypress/pageobject/Patient/PatientConsultation.ts @@ -16,12 +16,14 @@ export class PatientConsultationPage { } fillIllnessHistory(history: string) { + cy.wait(5000); cy.get("#history_of_present_illness").scrollIntoView(); cy.get("#history_of_present_illness").should("be.visible"); cy.get("#history_of_present_illness").click().type(history); } enterConsultationDetails( + category: string, examinationDetails: string, weight: string, height: string, @@ -30,6 +32,11 @@ export class PatientConsultationPage { verificationBy: string ) { cy.get("#symptoms").click(); + cy.get("#category") + .click() + .then(() => { + cy.get("[role='option']").contains(category).click(); + }); cy.get("#examination_details").click().type(examinationDetails); cy.get("#weight").click().type(height); cy.get("#height").click().type(weight); @@ -38,10 +45,12 @@ export class PatientConsultationPage { cy.get( "#icd11_diagnoses_object input[placeholder='Select'][role='combobox']" ) + .scrollIntoView() .click() .type("1A"); cy.get("#icd11_diagnoses_object [role='option']") .contains("1A03 Intestinal infections due to Escherichia coli") + .scrollIntoView() .click(); cy.get("label[for='icd11_diagnoses_object']").click(); cy.wait("@getIcdResults").its("response.statusCode").should("eq", 200); diff --git a/cypress/pageobject/Patient/PatientCreation.ts b/cypress/pageobject/Patient/PatientCreation.ts index 9b8df4a287e..4fcd43dc490 100644 --- a/cypress/pageobject/Patient/PatientCreation.ts +++ b/cypress/pageobject/Patient/PatientCreation.ts @@ -124,4 +124,8 @@ export class PatientPage { verifyStatusCode() { cy.wait("@getFacilities").its("response.statusCode").should("eq", 200); } + + patientformvisibility() { + cy.get("[data-testid='current-address']").scrollIntoView(); + } } diff --git a/package-lock.json b/package-lock.json index db11b8c5220..4871fbe374b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -32,7 +32,6 @@ "events": "^3.3.0", "i18next": "^23.2.7", "i18next-browser-languagedetector": "^7.1.0", - "libphonenumber-js": "^1.10.37", "lodash": "^4.17.21", "postcss-loader": "^7.3.3", "qrcode.react": "^3.1.0", @@ -50,7 +49,6 @@ "react-player": "^2.12.0", "react-qr-reader": "^2.2.1", "react-redux": "^8.1.1", - "react-swipeable-views": "^0.14.0", "react-transition-group": "^4.4.5", "react-webcam": "^7.1.1", "read-excel-file": "^5.6.1", @@ -86,7 +84,6 @@ "@types/react-dom": "^18.2.6", "@types/react-google-recaptcha": "^2.1.5", "@types/react-qr-reader": "^2.1.4", - "@types/react-swipeable-views": "^0.13.2", "@types/react-transition-group": "^4.4.6", "@typescript-eslint/eslint-plugin": "^5.61.0", "@typescript-eslint/parser": "^5.61.0", @@ -5640,15 +5637,6 @@ "@types/react": "*" } }, - "node_modules/@types/react-swipeable-views": { - "version": "0.13.2", - "resolved": "https://registry.npmjs.org/@types/react-swipeable-views/-/react-swipeable-views-0.13.2.tgz", - "integrity": "sha512-FiszBm9M0JicAgzO/IwDqpfHQRUEjPZA88UexYsVD6qHJBf5LrbGjR5Mw4+yZbf8ZxJneNqOsZbe4WGjOYG7iQ==", - "dev": true, - "dependencies": { - "@types/react": "*" - } - }, "node_modules/@types/react-transition-group": { "version": "4.4.6", "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.6.tgz", @@ -11997,11 +11985,6 @@ "node": ">=4.0" } }, - "node_modules/keycode": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/keycode/-/keycode-2.2.1.tgz", - "integrity": "sha512-Rdgz9Hl9Iv4QKi8b0OlCRQEzp4AgVxyCtz5S/+VIHezDmrDhkp2N2TqBWOLz0/gbeREXOOiI9/4b8BY9uw2vFg==" - }, "node_modules/kind-of": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", @@ -12079,11 +12062,6 @@ "node": ">= 0.8.0" } }, - "node_modules/libphonenumber-js": { - "version": "1.10.37", - "resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.10.37.tgz", - "integrity": "sha512-Z10PCaOCiAxbUxLyR31DNeeNugSVP6iv/m7UrSKS5JHziEMApJtgku4e9Q69pzzSC9LnQiM09sqsGf2ticZnMw==" - }, "node_modules/lilconfig": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", @@ -14279,6 +14257,7 @@ "node_modules/npm/node_modules/brace-expansion": { "version": "2.0.1", "dev": true, + "inBundle": true, "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" @@ -15273,15 +15252,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/npm/node_modules/minimatch/node_modules/brace-expansion": { - "version": "2.0.1", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, "node_modules/npm/node_modules/minipass": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", @@ -18131,19 +18101,6 @@ "integrity": "sha512-Fl7FuabXsJnV5Q1qIOQwx/sagGF18kogb4gpfcG4gjLBWO0WDiiz1ko/ExayuxE7InyQkBLkxRFG5oxY6Uu3Kg==", "dev": true }, - "node_modules/react-event-listener": { - "version": "0.6.6", - "resolved": "https://registry.npmjs.org/react-event-listener/-/react-event-listener-0.6.6.tgz", - "integrity": "sha512-+hCNqfy7o9wvO6UgjqFmBzARJS7qrNoda0VqzvOuioEpoEXKutiKuv92dSz6kP7rYLmyHPyYNLesi5t/aH1gfw==", - "dependencies": { - "@babel/runtime": "^7.2.0", - "prop-types": "^15.6.0", - "warning": "^4.0.1" - }, - "peerDependencies": { - "react": "^16.3.0" - } - }, "node_modules/react-fast-compare": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.2.tgz", @@ -18312,91 +18269,6 @@ "node": ">=0.10.0" } }, - "node_modules/react-swipeable-views": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/react-swipeable-views/-/react-swipeable-views-0.14.0.tgz", - "integrity": "sha512-wrTT6bi2nC3JbmyNAsPXffUXLn0DVT9SbbcFr36gKpbaCgEp7rX/OFxsu5hPc/NBsUhHyoSRGvwqJNNrWTwCww==", - "dependencies": { - "@babel/runtime": "7.0.0", - "prop-types": "^15.5.4", - "react-swipeable-views-core": "^0.14.0", - "react-swipeable-views-utils": "^0.14.0", - "warning": "^4.0.1" - }, - "engines": { - "node": ">=6.0.0" - }, - "peerDependencies": { - "react": "^15.3.0 || ^16.0.0 || ^17.0.0" - } - }, - "node_modules/react-swipeable-views-core": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/react-swipeable-views-core/-/react-swipeable-views-core-0.14.0.tgz", - "integrity": "sha512-0W/e9uPweNEOSPjmYtuKSC/SvKKg1sfo+WtPdnxeLF3t2L82h7jjszuOHz9C23fzkvLfdgkaOmcbAxE9w2GEjA==", - "dependencies": { - "@babel/runtime": "7.0.0", - "warning": "^4.0.1" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/react-swipeable-views-core/node_modules/@babel/runtime": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.0.0.tgz", - "integrity": "sha512-7hGhzlcmg01CvH1EHdSPVXYX1aJ8KCEyz6I9xYIi/asDtzBPMyMhVibhM/K6g/5qnKBwjZtp10bNZIEFTRW1MA==", - "dependencies": { - "regenerator-runtime": "^0.12.0" - } - }, - "node_modules/react-swipeable-views-core/node_modules/regenerator-runtime": { - "version": "0.12.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.12.1.tgz", - "integrity": "sha512-odxIc1/vDlo4iZcfXqRYFj0vpXFNoGdKMAUieAlFYO6m/nl5e9KR/beGf41z4a1FI+aQgtjhuaSlDxQ0hmkrHg==" - }, - "node_modules/react-swipeable-views-utils": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/react-swipeable-views-utils/-/react-swipeable-views-utils-0.14.0.tgz", - "integrity": "sha512-W+fXBOsDqgFK1/g7MzRMVcDurp3LqO3ksC8UgInh2P/tKgb5DusuuB1geKHFc6o1wKl+4oyER4Zh3Lxmr8xbXA==", - "dependencies": { - "@babel/runtime": "7.0.0", - "keycode": "^2.1.7", - "prop-types": "^15.6.0", - "react-event-listener": "^0.6.0", - "react-swipeable-views-core": "^0.14.0", - "shallow-equal": "^1.2.1" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/react-swipeable-views-utils/node_modules/@babel/runtime": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.0.0.tgz", - "integrity": "sha512-7hGhzlcmg01CvH1EHdSPVXYX1aJ8KCEyz6I9xYIi/asDtzBPMyMhVibhM/K6g/5qnKBwjZtp10bNZIEFTRW1MA==", - "dependencies": { - "regenerator-runtime": "^0.12.0" - } - }, - "node_modules/react-swipeable-views-utils/node_modules/regenerator-runtime": { - "version": "0.12.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.12.1.tgz", - "integrity": "sha512-odxIc1/vDlo4iZcfXqRYFj0vpXFNoGdKMAUieAlFYO6m/nl5e9KR/beGf41z4a1FI+aQgtjhuaSlDxQ0hmkrHg==" - }, - "node_modules/react-swipeable-views/node_modules/@babel/runtime": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.0.0.tgz", - "integrity": "sha512-7hGhzlcmg01CvH1EHdSPVXYX1aJ8KCEyz6I9xYIi/asDtzBPMyMhVibhM/K6g/5qnKBwjZtp10bNZIEFTRW1MA==", - "dependencies": { - "regenerator-runtime": "^0.12.0" - } - }, - "node_modules/react-swipeable-views/node_modules/regenerator-runtime": { - "version": "0.12.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.12.1.tgz", - "integrity": "sha512-odxIc1/vDlo4iZcfXqRYFj0vpXFNoGdKMAUieAlFYO6m/nl5e9KR/beGf41z4a1FI+aQgtjhuaSlDxQ0hmkrHg==" - }, "node_modules/react-transition-group": { "version": "4.4.5", "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", @@ -19377,11 +19249,6 @@ "node": ">=8" } }, - "node_modules/shallow-equal": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/shallow-equal/-/shallow-equal-1.2.1.tgz", - "integrity": "sha512-S4vJDjHHMBaiZuT9NPb616CSmLf618jawtv3sufLl6ivK8WocjAo58cXwbRV1cgqxH0Qbv+iUt6m05eqEa2IRA==" - }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -21274,14 +21141,6 @@ "makeerror": "1.0.12" } }, - "node_modules/warning": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz", - "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==", - "dependencies": { - "loose-envify": "^1.0.0" - } - }, "node_modules/watchpack": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", diff --git a/package.json b/package.json index 2e894f22288..246734dc3d4 100644 --- a/package.json +++ b/package.json @@ -72,7 +72,6 @@ "events": "^3.3.0", "i18next": "^23.2.7", "i18next-browser-languagedetector": "^7.1.0", - "libphonenumber-js": "^1.10.37", "lodash": "^4.17.21", "postcss-loader": "^7.3.3", "qrcode.react": "^3.1.0", @@ -90,7 +89,6 @@ "react-player": "^2.12.0", "react-qr-reader": "^2.2.1", "react-redux": "^8.1.1", - "react-swipeable-views": "^0.14.0", "react-transition-group": "^4.4.5", "react-webcam": "^7.1.1", "read-excel-file": "^5.6.1", @@ -126,7 +124,6 @@ "@types/react-dom": "^18.2.6", "@types/react-google-recaptcha": "^2.1.5", "@types/react-qr-reader": "^2.1.4", - "@types/react-swipeable-views": "^0.13.2", "@types/react-transition-group": "^4.4.6", "@typescript-eslint/eslint-plugin": "^5.61.0", "@typescript-eslint/parser": "^5.61.0", diff --git a/src/App.tsx b/src/App.tsx index e00ca18f652..f89dfd11e74 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,111 +1,28 @@ -import * as Sentry from "@sentry/browser"; - -import { FC, Suspense, lazy, useEffect, useState } from "react"; -import { getConfig, getCurrentUser } from "./Redux/actions"; -import { statusType, useAbortableEffect } from "./Common/utils"; -import { useDispatch, useSelector } from "react-redux"; - -import AppRouter from "./Router/AppRouter"; -import { HistoryAPIProvider } from "./CAREUI/misc/HistoryAPIProvider"; -import { AppConfigContext, IConfig } from "./Common/hooks/useConfig"; -import { LocalStorageKeys } from "./Common/constants"; -import Plausible from "./Components/Common/Plausible"; -import SessionRouter from "./Router/SessionRouter"; -import axios from "axios"; -import { AuthUserContext } from "./Common/hooks/useAuthUser"; - -const Loading = lazy(() => import("./Components/Common/Loading")); - -const App: FC = () => { - const dispatch: any = useDispatch(); - const state: any = useSelector((state) => state); - const { currentUser, config } = state; - const [user, setUser] = useState(null); - - useAbortableEffect(async () => { - const res = await dispatch(getConfig()); - if (res.data && res.status < 400) { - const config = res.data as IConfig; - - if (config?.sentry_dsn && import.meta.env.PROD) { - Sentry.init({ - environment: config.sentry_environment, - dsn: config.sentry_dsn, - }); - } - - localStorage.setItem("config", JSON.stringify(config)); - } - }, [dispatch]); - - const updateRefreshToken = () => { - const refresh = localStorage.getItem(LocalStorageKeys.refreshToken); - // const access = localStorage.getItem(LocalStorageKeys.accessToken); - // if (!access && refresh) { - // localStorage.removeItem(LocalStorageKeys.refreshToken); - // document.location.reload(); - // return; - // } - if (!refresh) { - return; - } - axios - .post("/api/v1/auth/token/refresh/", { - refresh, - }) - .then((resp) => { - localStorage.setItem(LocalStorageKeys.accessToken, resp.data.access); - localStorage.setItem(LocalStorageKeys.refreshToken, resp.data.refresh); - }); - }; - useEffect(() => { - updateRefreshToken(); - setInterval(updateRefreshToken, 5 * 60 * 1000); - }, [user]); - - useAbortableEffect( - async (status: statusType) => { - const res = await dispatch(getCurrentUser()); - if (!status.aborted && res && res.statusCode === 200) { - setUser(res.data); - } - }, - [dispatch] - ); - - useEffect(() => { - const darkThemeMq = window.matchMedia("(prefers-color-scheme: dark)"); - const favicon: any = document.querySelector("link[rel~='icon']"); - if (darkThemeMq.matches) { - favicon.href = "/favicon-light.ico"; - } else { - favicon.href = "/favicon.ico"; - } - }, []); - - if ( - !currentUser || - currentUser.isFetching || - !config || - config.isFetching || - !config.data - ) { - return ; - } - +import { Suspense } from "react"; +import Routers from "./Routers"; +import { + AppConfigProvider, + AuthUserProvider, + HistoryAPIProvider, +} from "./Providers"; +import ThemedFavicon from "./CAREUI/misc/ThemedFavicon"; +import Intergrations from "./Integrations"; +import Loading from "./Components/Common/Loading"; + +const App = () => { return ( }> + - - {currentUser?.data ? ( - - - - ) : ( - - )} - - + + }> + + + + {/* Integrations */} + + + ); diff --git a/src/CAREUI/misc/ThemedFavicon.tsx b/src/CAREUI/misc/ThemedFavicon.tsx new file mode 100644 index 00000000000..55b8d34ec7e --- /dev/null +++ b/src/CAREUI/misc/ThemedFavicon.tsx @@ -0,0 +1,14 @@ +import { useEffect } from "react"; + +export default function ThemedFavicon() { + useEffect(() => { + const darkThemeMq = window.matchMedia("(prefers-color-scheme: dark)"); + const favicon = document.querySelector( + "link[rel~='icon']" + ) as HTMLLinkElement; + + favicon.href = darkThemeMq.matches ? "/favicon-light.ico" : "/favicon.ico"; + }, []); + + return null; +} diff --git a/src/Common/constants.tsx b/src/Common/constants.tsx index f5801e97e4b..f64a9fa27ae 100644 --- a/src/Common/constants.tsx +++ b/src/Common/constants.tsx @@ -1,9 +1,9 @@ import { IConfig } from "./hooks/useConfig"; import { PatientCategory } from "../Components/Facility/models"; import { SortOption } from "../Components/Common/SortDropdown"; -import { parsePhoneNumberFromString } from "libphonenumber-js"; import { dateQueryString } from "../Utils/utils"; import { IconName } from "../CAREUI/icons/CareIcon"; +import { PhoneNumberValidator } from "../Components/Form/FieldValidators"; export const RESULTS_PER_PAGE_LIMIT = 14; export const PAGINATION_LIMIT = 36; @@ -663,22 +663,22 @@ export const NURSING_CARE_FIELDS: Array = [ export const EYE_OPEN_SCALE = [ { value: 4, text: "Spontaneous" }, { value: 3, text: "To Speech" }, - { value: 2, text: "Pain" }, - { value: 1, text: "None" }, + { value: 2, text: "To Pain" }, + { value: 1, text: "No Response" }, ]; export const VERBAL_RESPONSE_SCALE = [ - { value: 5, text: "Oriented/Coos/Babbles" }, + { value: 5, text: "Oriented to Time, Place and Person" }, { value: 4, text: "Confused/Irritable" }, { value: 3, text: "Inappropriate words/Cry to Pain" }, { value: 2, text: "Incomprehensible words/Moans to pain" }, - { value: 1, text: "None" }, + { value: 1, text: "No Response" }, ]; export const MOTOR_RESPONSE_SCALE = [ - { value: 6, text: "Obeying commands" }, - { value: 5, text: "Moves to localised pain" }, - { value: 4, text: "Flexion withdrawal from pain" }, + { value: 6, text: "Obeying commands/Normal acrivity" }, + { value: 5, text: "Moves to localized pain" }, + { value: 4, text: "Flexion/Withdrawal from pain" }, { value: 3, text: "Abnormal Flexion(decorticate)" }, { value: 2, text: "Abnormal Extension(decerebrate)" }, { value: 1, text: "No Response" }, @@ -938,13 +938,12 @@ export const XLSXAssetImportSchema = { prop: "support_phone", type: String, parse: (phone: number | string) => { - const parsed = parsePhoneNumberFromString(String(phone), "IN"); - - if (!parsed?.isValid()) { + phone = "+91" + String(phone); + if (!PhoneNumberValidator(["support"])(phone) === undefined) { throw new Error("Invalid Support Phone Number"); } - return parsed?.format("E.164"); + return phone ? phone : undefined; }, required: true, }, @@ -1001,3 +1000,126 @@ export const XLSXAssetImportSchema = { }, }, }; + +export const AREACODES: Record = { + CA: [ + "403", + "587", + "250", + "604", + "778", + "204", + "431", + "506", + "709", + "867", + "902", + "226", + "249", + "289", + "343", + "365", + "416", + "437", + "519", + "613", + "647", + "705", + "807", + "902", + "418", + "438", + "450", + "514", + "579", + "581", + "819", + "306", + "639", + "867", + ], + JM: ["658", "876"], + PR: ["787", "939"], + DO: ["809", "829"], + RE: ["262", "263", "692", "693"], + YT: ["269", "639"], + CC: ["89162"], + CX: ["89164"], + BQ: ["9"], + KZ: ["6", "7"], + SJ: ["79"], +}; + +export const IN_LANDLINE_AREA_CODES = [ + "11", + "22", + "33", + "44", + "20", + "40", + "79", + "80", + "120", + "124", + "129", + "135", + "141", + "160", + "161", + "172", + "175", + "181", + "183", + "233", + "240", + "241", + "250", + "251", + "253", + "257", + "260", + "261", + "265", + "343", + "413", + "422", + "431", + "435", + "452", + "462", + "471", + "474", + "477", + "478", + "481", + "484", + "485", + "487", + "490", + "497", + "512", + "522", + "532", + "542", + "551", + "562", + "581", + "591", + "621", + "612", + "641", + "657", + "712", + "721", + "724", + "751", + "761", + "821", + "824", + "831", + "836", + "866", + "870", + "891", + "4822", +]; diff --git a/src/Common/hooks/useAppHistory.ts b/src/Common/hooks/useAppHistory.ts index f4ee2f11f78..a4605db082b 100644 --- a/src/Common/hooks/useAppHistory.ts +++ b/src/Common/hooks/useAppHistory.ts @@ -3,7 +3,7 @@ import { useContext } from "react"; import { HistoryContext, ResetHistoryContext, -} from "../../CAREUI/misc/HistoryAPIProvider"; +} from "../../Providers/HistoryAPIProvider"; export default function useAppHistory() { const history = useContext(HistoryContext); diff --git a/src/Common/hooks/useFilters.tsx b/src/Common/hooks/useFilters.tsx index 309cdc37787..129a696041b 100644 --- a/src/Common/hooks/useFilters.tsx +++ b/src/Common/hooks/useFilters.tsx @@ -196,7 +196,7 @@ export default function useFilters({ limit = 14 }: { limit?: number }) { FilterBadge, FilterBadges, Pagination, - // TODO: update this props to be compliant with new FiltersSlideOver when #3996 is merged. + advancedFilter: { show: showFilters, setShow: setShowFilters, diff --git a/src/Common/hooks/useRangePagination.ts b/src/Common/hooks/useRangePagination.ts index 7652ae546c1..e6bbe9f573e 100644 --- a/src/Common/hooks/useRangePagination.ts +++ b/src/Common/hooks/useRangePagination.ts @@ -9,17 +9,18 @@ interface Props { bounds: DateRange; perPage: number; slots?: number; - defaultEnd?: boolean; + snapToLatest?: boolean; + reverse?: boolean; } const useRangePagination = ({ bounds, perPage, ...props }: Props) => { const [currentRange, setCurrentRange] = useState( - getInitialBounds(bounds, perPage, props.defaultEnd) + getInitialBounds(bounds, perPage, props.snapToLatest) ); useEffect(() => { - setCurrentRange(getInitialBounds(bounds, perPage, props.defaultEnd)); - }, [bounds, perPage, props.defaultEnd]); + setCurrentRange(getInitialBounds(bounds, perPage, props.snapToLatest)); + }, [bounds, perPage, props.snapToLatest]); const next = () => { const { end } = currentRange; @@ -62,17 +63,24 @@ const useRangePagination = ({ bounds, perPage, ...props }: Props) => { } const slots: DateRange[] = []; - const { start } = currentRange; + const { start, end } = currentRange; const delta = perPage / props.slots; for (let i = 0; i < props.slots; i++) { - slots.push({ - start: new Date(start.valueOf() + delta * i), - end: new Date(start.valueOf() + delta * (i + 1)), - }); + if (props.snapToLatest) { + slots.push({ + start: new Date(end.valueOf() - delta * (i - 1)), + end: new Date(end.valueOf() - delta * i), + }); + } else { + slots.push({ + start: new Date(start.valueOf() + delta * i), + end: new Date(start.valueOf() + delta * (i + 1)), + }); + } } - return slots; + return props.reverse ? slots.reverse() : slots; }, [currentRange, props.slots, perPage]); return { @@ -90,7 +98,7 @@ export default useRangePagination; const getInitialBounds = ( bounds: DateRange, perPage: number, - defaultEnd?: boolean + snapToLatest?: boolean ) => { const deltaBounds = bounds.end.valueOf() - bounds.start.valueOf(); @@ -98,7 +106,7 @@ const getInitialBounds = ( return bounds; } - if (defaultEnd) { + if (snapToLatest) { return { start: new Date(bounds.end.valueOf() - perPage), end: bounds.end, diff --git a/src/Common/static/countryPhoneAndFlags.json b/src/Common/static/countryPhoneAndFlags.json index 9b2ea40427d..87d963444b5 100644 --- a/src/Common/static/countryPhoneAndFlags.json +++ b/src/Common/static/countryPhoneAndFlags.json @@ -2,20 +2,20 @@ "AD": { "flag": "๐Ÿ‡ฆ๐Ÿ‡ฉ", "name": "Andorra", "code": "376" }, "AE": { "flag": "๐Ÿ‡ฆ๐Ÿ‡ช", "name": "United Arab Emirates", "code": "971" }, "AF": { "flag": "๐Ÿ‡ฆ๐Ÿ‡ซ", "name": "Afghanistan", "code": "93" }, - "AG": { "flag": "๐Ÿ‡ฆ๐Ÿ‡ฌ", "name": "Antigua & Barbuda", "code": "+1-268" }, - "AI": { "flag": "๐Ÿ‡ฆ๐Ÿ‡ฎ", "name": "Anguilla", "code": "+1-264" }, + "AG": { "flag": "๐Ÿ‡ฆ๐Ÿ‡ฌ", "name": "Antigua & Barbuda", "code": "1-268" }, + "AI": { "flag": "๐Ÿ‡ฆ๐Ÿ‡ฎ", "name": "Anguilla", "code": "1-264" }, "AL": { "flag": "๐Ÿ‡ฆ๐Ÿ‡ฑ", "name": "Albania", "code": "355" }, "AM": { "flag": "๐Ÿ‡ฆ๐Ÿ‡ฒ", "name": "Armenia", "code": "374" }, "AO": { "flag": "๐Ÿ‡ฆ๐Ÿ‡ด", "name": "Angola", "code": "244" }, "AR": { "flag": "๐Ÿ‡ฆ๐Ÿ‡ท", "name": "Argentina", "code": "54" }, - "AS": { "flag": "๐Ÿ‡ฆ๐Ÿ‡ธ", "name": "American Samoa", "code": "+1-684" }, + "AS": { "flag": "๐Ÿ‡ฆ๐Ÿ‡ธ", "name": "American Samoa", "code": "1-684" }, "AT": { "flag": "๐Ÿ‡ฆ๐Ÿ‡น", "name": "Austria", "code": "43" }, "AU": { "flag": "๐Ÿ‡ฆ๐Ÿ‡บ", "name": "Australia", "code": "61" }, "AW": { "flag": "๐Ÿ‡ฆ๐Ÿ‡ผ", "name": "Aruba", "code": "297" }, - "AX": { "flag": "๐Ÿ‡ฆ๐Ÿ‡ฝ", "name": "ร…land Islands", "code": "+358-18" }, + "AX": { "flag": "๐Ÿ‡ฆ๐Ÿ‡ฝ", "name": "ร…land Islands", "code": "358-18" }, "AZ": { "flag": "๐Ÿ‡ฆ๐Ÿ‡ฟ", "name": "Azerbaijan", "code": "994" }, "BA": { "flag": "๐Ÿ‡ง๐Ÿ‡ฆ", "name": "Bosnia & Herzegovina", "code": "387" }, - "BB": { "flag": "๐Ÿ‡ง๐Ÿ‡ง", "name": "Barbados", "code": "+1-246" }, + "BB": { "flag": "๐Ÿ‡ง๐Ÿ‡ง", "name": "Barbados", "code": "1-246" }, "BD": { "flag": "๐Ÿ‡ง๐Ÿ‡ฉ", "name": "Bangladesh", "code": "880" }, "BE": { "flag": "๐Ÿ‡ง๐Ÿ‡ช", "name": "Belgium", "code": "32" }, "BF": { "flag": "๐Ÿ‡ง๐Ÿ‡ซ", "name": "Burkina Faso", "code": "226" }, @@ -24,12 +24,12 @@ "BI": { "flag": "๐Ÿ‡ง๐Ÿ‡ฎ", "name": "Burundi", "code": "257" }, "BJ": { "flag": "๐Ÿ‡ง๐Ÿ‡ฏ", "name": "Benin", "code": "229" }, "BL": { "flag": "๐Ÿ‡ง๐Ÿ‡ฑ", "name": "St. Barthรฉlemy", "code": "590" }, - "BM": { "flag": "๐Ÿ‡ง๐Ÿ‡ฒ", "name": "Bermuda", "code": "+1-441" }, + "BM": { "flag": "๐Ÿ‡ง๐Ÿ‡ฒ", "name": "Bermuda", "code": "1-441" }, "BN": { "flag": "๐Ÿ‡ง๐Ÿ‡ณ", "name": "Brunei", "code": "673" }, "BO": { "flag": "๐Ÿ‡ง๐Ÿ‡ด", "name": "Bolivia", "code": "591" }, "BQ": { "flag": "๐Ÿ‡ง๐Ÿ‡ถ", "name": "Caribbean Netherlands", "code": "599" }, "BR": { "flag": "๐Ÿ‡ง๐Ÿ‡ท", "name": "Brazil", "code": "55" }, - "BS": { "flag": "๐Ÿ‡ง๐Ÿ‡ธ", "name": "Bahamas", "code": "+1-242" }, + "BS": { "flag": "๐Ÿ‡ง๐Ÿ‡ธ", "name": "Bahamas", "code": "1-242" }, "BT": { "flag": "๐Ÿ‡ง๐Ÿ‡น", "name": "Bhutan", "code": "975" }, "BW": { "flag": "๐Ÿ‡ง๐Ÿ‡ผ", "name": "Botswana", "code": "267" }, "BY": { "flag": "๐Ÿ‡ง๐Ÿ‡พ", "name": "Belarus", "code": "375" }, @@ -56,11 +56,11 @@ "DE": { "flag": "๐Ÿ‡ฉ๐Ÿ‡ช", "name": "Germany", "code": "49" }, "DJ": { "flag": "๐Ÿ‡ฉ๐Ÿ‡ฏ", "name": "Djibouti", "code": "253" }, "DK": { "flag": "๐Ÿ‡ฉ๐Ÿ‡ฐ", "name": "Denmark", "code": "45" }, - "DM": { "flag": "๐Ÿ‡ฉ๐Ÿ‡ฒ", "name": "Dominica", "code": "+1-767" }, + "DM": { "flag": "๐Ÿ‡ฉ๐Ÿ‡ฒ", "name": "Dominica", "code": "1-767" }, "DO": { "flag": "๐Ÿ‡ฉ๐Ÿ‡ด", "name": "Dominican Republic", - "code": "+1-809 and 1-829" + "code": "1" }, "DZ": { "flag": "๐Ÿ‡ฉ๐Ÿ‡ฟ", "name": "Algeria", "code": "213" }, "EC": { "flag": "๐Ÿ‡ช๐Ÿ‡จ", "name": "Ecuador", "code": "593" }, @@ -78,10 +78,10 @@ "FR": { "flag": "๐Ÿ‡ซ๐Ÿ‡ท", "name": "France", "code": "33" }, "GA": { "flag": "๐Ÿ‡ฌ๐Ÿ‡ฆ", "name": "Gabon", "code": "241" }, "GB": { "flag": "๐Ÿ‡ฌ๐Ÿ‡ง", "name": "United Kingdom", "code": "44" }, - "GD": { "flag": "๐Ÿ‡ฌ๐Ÿ‡ฉ", "name": "Grenada", "code": "+1-473" }, + "GD": { "flag": "๐Ÿ‡ฌ๐Ÿ‡ฉ", "name": "Grenada", "code": "1-473" }, "GE": { "flag": "๐Ÿ‡ฌ๐Ÿ‡ช", "name": "Georgia", "code": "995" }, "GF": { "flag": "๐Ÿ‡ฌ๐Ÿ‡ซ", "name": "French Guiana", "code": "594" }, - "GG": { "flag": "๐Ÿ‡ฌ๐Ÿ‡ฌ", "name": "Guernsey", "code": "+44-1481" }, + "GG": { "flag": "๐Ÿ‡ฌ๐Ÿ‡ฌ", "name": "Guernsey", "code": "44-1481" }, "GH": { "flag": "๐Ÿ‡ฌ๐Ÿ‡ญ", "name": "Ghana", "code": "233" }, "GI": { "flag": "๐Ÿ‡ฌ๐Ÿ‡ฎ", "name": "Gibraltar", "code": "350" }, "GL": { "flag": "๐Ÿ‡ฌ๐Ÿ‡ฑ", "name": "Greenland", "code": "299" }, @@ -91,7 +91,7 @@ "GQ": { "flag": "๐Ÿ‡ฌ๐Ÿ‡ถ", "name": "Equatorial Guinea", "code": "240" }, "GR": { "flag": "๐Ÿ‡ฌ๐Ÿ‡ท", "name": "Greece", "code": "30" }, "GT": { "flag": "๐Ÿ‡ฌ๐Ÿ‡น", "name": "Guatemala", "code": "502" }, - "GU": { "flag": "๐Ÿ‡ฌ๐Ÿ‡บ", "name": "Guam", "code": "+1-671" }, + "GU": { "flag": "๐Ÿ‡ฌ๐Ÿ‡บ", "name": "Guam", "code": "1-671" }, "GW": { "flag": "๐Ÿ‡ฌ๐Ÿ‡ผ", "name": "Guinea-Bissau", "code": "245" }, "GY": { "flag": "๐Ÿ‡ฌ๐Ÿ‡พ", "name": "Guyana", "code": "592" }, "HK": { "flag": "๐Ÿ‡ญ๐Ÿ‡ฐ", "name": "Hong Kong SAR China", "code": "852" }, @@ -103,7 +103,7 @@ "ID": { "flag": "๐Ÿ‡ฎ๐Ÿ‡ฉ", "name": "Indonesia", "code": "62" }, "IE": { "flag": "๐Ÿ‡ฎ๐Ÿ‡ช", "name": "Ireland", "code": "353" }, "IL": { "flag": "๐Ÿ‡ฎ๐Ÿ‡ฑ", "name": "Israel", "code": "972" }, - "IM": { "flag": "๐Ÿ‡ฎ๐Ÿ‡ฒ", "name": "Isle of Man", "code": "+44-1624" }, + "IM": { "flag": "๐Ÿ‡ฎ๐Ÿ‡ฒ", "name": "Isle of Man", "code": "44-1624" }, "IN": { "flag": "๐Ÿ‡ฎ๐Ÿ‡ณ", "name": "India", "code": "91" }, "IO": { "flag": "๐Ÿ‡ฎ๐Ÿ‡ด", @@ -114,8 +114,8 @@ "IR": { "flag": "๐Ÿ‡ฎ๐Ÿ‡ท", "name": "Iran", "code": "98" }, "IS": { "flag": "๐Ÿ‡ฎ๐Ÿ‡ธ", "name": "Iceland", "code": "354" }, "IT": { "flag": "๐Ÿ‡ฎ๐Ÿ‡น", "name": "Italy", "code": "39" }, - "JE": { "flag": "๐Ÿ‡ฏ๐Ÿ‡ช", "name": "Jersey", "code": "+44-1534" }, - "JM": { "flag": "๐Ÿ‡ฏ๐Ÿ‡ฒ", "name": "Jamaica", "code": "+1-876" }, + "JE": { "flag": "๐Ÿ‡ฏ๐Ÿ‡ช", "name": "Jersey", "code": "44-1534" }, + "JM": { "flag": "๐Ÿ‡ฏ๐Ÿ‡ฒ", "name": "Jamaica", "code": "1" }, "JO": { "flag": "๐Ÿ‡ฏ๐Ÿ‡ด", "name": "Jordan", "code": "962" }, "JP": { "flag": "๐Ÿ‡ฏ๐Ÿ‡ต", "name": "Japan", "code": "81" }, "KE": { "flag": "๐Ÿ‡ฐ๐Ÿ‡ช", "name": "Kenya", "code": "254" }, @@ -123,15 +123,15 @@ "KH": { "flag": "๐Ÿ‡ฐ๐Ÿ‡ญ", "name": "Cambodia", "code": "855" }, "KI": { "flag": "๐Ÿ‡ฐ๐Ÿ‡ฎ", "name": "Kiribati", "code": "686" }, "KM": { "flag": "๐Ÿ‡ฐ๐Ÿ‡ฒ", "name": "Comoros", "code": "269" }, - "KN": { "flag": "๐Ÿ‡ฐ๐Ÿ‡ณ", "name": "St. Kitts & Nevis", "code": "+1-869" }, + "KN": { "flag": "๐Ÿ‡ฐ๐Ÿ‡ณ", "name": "St. Kitts & Nevis", "code": "1-869" }, "KP": { "flag": "๐Ÿ‡ฐ๐Ÿ‡ต", "name": "North Korea", "code": "850" }, "KR": { "flag": "๐Ÿ‡ฐ๐Ÿ‡ท", "name": "South Korea", "code": "82" }, "KW": { "flag": "๐Ÿ‡ฐ๐Ÿ‡ผ", "name": "Kuwait", "code": "965" }, - "KY": { "flag": "๐Ÿ‡ฐ๐Ÿ‡พ", "name": "Cayman Islands", "code": "+1-345" }, + "KY": { "flag": "๐Ÿ‡ฐ๐Ÿ‡พ", "name": "Cayman Islands", "code": "1-345" }, "KZ": { "flag": "๐Ÿ‡ฐ๐Ÿ‡ฟ", "name": "Kazakhstan", "code": "7" }, "LA": { "flag": "๐Ÿ‡ฑ๐Ÿ‡ฆ", "name": "Laos", "code": "856" }, "LB": { "flag": "๐Ÿ‡ฑ๐Ÿ‡ง", "name": "Lebanon", "code": "961" }, - "LC": { "flag": "๐Ÿ‡ฑ๐Ÿ‡จ", "name": "St. Lucia", "code": "+1-758" }, + "LC": { "flag": "๐Ÿ‡ฑ๐Ÿ‡จ", "name": "St. Lucia", "code": "1-758" }, "LI": { "flag": "๐Ÿ‡ฑ๐Ÿ‡ฎ", "name": "Liechtenstein", "code": "423" }, "LK": { "flag": "๐Ÿ‡ฑ๐Ÿ‡ฐ", "name": "Sri Lanka", "code": "94" }, "LR": { "flag": "๐Ÿ‡ฑ๐Ÿ‡ท", "name": "Liberia", "code": "231" }, @@ -152,10 +152,10 @@ "MM": { "flag": "๐Ÿ‡ฒ๐Ÿ‡ฒ", "name": "Myanmar (Burma)", "code": "95" }, "MN": { "flag": "๐Ÿ‡ฒ๐Ÿ‡ณ", "name": "Mongolia", "code": "976" }, "MO": { "flag": "๐Ÿ‡ฒ๐Ÿ‡ด", "name": "Macao SAR China", "code": "853" }, - "MP": { "flag": "๐Ÿ‡ฒ๐Ÿ‡ต", "name": "Northern Mariana Islands", "code": "+1-670" }, + "MP": { "flag": "๐Ÿ‡ฒ๐Ÿ‡ต", "name": "Northern Mariana Islands", "code": "1-670" }, "MQ": { "flag": "๐Ÿ‡ฒ๐Ÿ‡ถ", "name": "Martinique", "code": "596" }, "MR": { "flag": "๐Ÿ‡ฒ๐Ÿ‡ท", "name": "Mauritania", "code": "222" }, - "MS": { "flag": "๐Ÿ‡ฒ๐Ÿ‡ธ", "name": "Montserrat", "code": "+1-664" }, + "MS": { "flag": "๐Ÿ‡ฒ๐Ÿ‡ธ", "name": "Montserrat", "code": "1-664" }, "MT": { "flag": "๐Ÿ‡ฒ๐Ÿ‡น", "name": "Malta", "code": "356" }, "MU": { "flag": "๐Ÿ‡ฒ๐Ÿ‡บ", "name": "Mauritius", "code": "230" }, "MV": { "flag": "๐Ÿ‡ฒ๐Ÿ‡ป", "name": "Maldives", "code": "960" }, @@ -185,7 +185,7 @@ "PL": { "flag": "๐Ÿ‡ต๐Ÿ‡ฑ", "name": "Poland", "code": "48" }, "PM": { "flag": "๐Ÿ‡ต๐Ÿ‡ฒ", "name": "St. Pierre & Miquelon", "code": "508" }, "PN": { "flag": "๐Ÿ‡ต๐Ÿ‡ณ", "name": "Pitcairn Islands", "code": "870" }, - "PR": { "flag": "๐Ÿ‡ต๐Ÿ‡ท", "name": "Puerto Rico", "code": "+1-787 and 1-939" }, + "PR": { "flag": "๐Ÿ‡ต๐Ÿ‡ท", "name": "Puerto Rico", "code": "1" }, "PS": { "flag": "๐Ÿ‡ต๐Ÿ‡ธ", "name": "Palestinian Territories", "code": "970" }, "PT": { "flag": "๐Ÿ‡ต๐Ÿ‡น", "name": "Portugal", "code": "351" }, "PW": { "flag": "๐Ÿ‡ต๐Ÿ‡ผ", "name": "Palau", "code": "680" }, @@ -214,10 +214,10 @@ "SS": { "flag": "๐Ÿ‡ธ๐Ÿ‡ธ", "name": "South Sudan", "code": "211" }, "ST": { "flag": "๐Ÿ‡ธ๐Ÿ‡น", "name": "Sรฃo Tomรฉ & Prรญncipe", "code": "239" }, "SV": { "flag": "๐Ÿ‡ธ๐Ÿ‡ป", "name": "El Salvador", "code": "503" }, - "SX": { "flag": "๐Ÿ‡ธ๐Ÿ‡ฝ", "name": "Sint Maarten", "code": "599" }, + "SX": { "flag": "๐Ÿ‡ธ๐Ÿ‡ฝ", "name": "Sint Maarten", "code": "1-721" }, "SY": { "flag": "๐Ÿ‡ธ๐Ÿ‡พ", "name": "Syria", "code": "963" }, "SZ": { "flag": "๐Ÿ‡ธ๐Ÿ‡ฟ", "name": "Eswatini", "code": "268" }, - "TC": { "flag": "๐Ÿ‡น๐Ÿ‡จ", "name": "Turks & Caicos Islands", "code": "+1-649" }, + "TC": { "flag": "๐Ÿ‡น๐Ÿ‡จ", "name": "Turks & Caicos Islands", "code": "1-649" }, "TD": { "flag": "๐Ÿ‡น๐Ÿ‡ฉ", "name": "Chad", "code": "235" }, "TG": { "flag": "๐Ÿ‡น๐Ÿ‡ฌ", "name": "Togo", "code": "228" }, "TH": { "flag": "๐Ÿ‡น๐Ÿ‡ญ", "name": "Thailand", "code": "66" }, @@ -228,7 +228,7 @@ "TN": { "flag": "๐Ÿ‡น๐Ÿ‡ณ", "name": "Tunisia", "code": "216" }, "TO": { "flag": "๐Ÿ‡น๐Ÿ‡ด", "name": "Tonga", "code": "676" }, "TR": { "flag": "๐Ÿ‡น๐Ÿ‡ท", "name": "Turkey", "code": "90" }, - "TT": { "flag": "๐Ÿ‡น๐Ÿ‡น", "name": "Trinidad & Tobago", "code": "+1-868" }, + "TT": { "flag": "๐Ÿ‡น๐Ÿ‡น", "name": "Trinidad & Tobago", "code": "1-868" }, "TV": { "flag": "๐Ÿ‡น๐Ÿ‡ป", "name": "Tuvalu", "code": "688" }, "TW": { "flag": "๐Ÿ‡น๐Ÿ‡ผ", "name": "Taiwan", "code": "886" }, "TZ": { "flag": "๐Ÿ‡น๐Ÿ‡ฟ", "name": "Tanzania", "code": "255" }, @@ -239,10 +239,10 @@ "UY": { "flag": "๐Ÿ‡บ๐Ÿ‡พ", "name": "Uruguay", "code": "598" }, "UZ": { "flag": "๐Ÿ‡บ๐Ÿ‡ฟ", "name": "Uzbekistan", "code": "998" }, "VA": { "flag": "๐Ÿ‡ป๐Ÿ‡ฆ", "name": "Vatican City", "code": "379" }, - "VC": { "flag": "๐Ÿ‡ป๐Ÿ‡จ", "name": "St. Vincent & Grenadines", "code": "+1-784" }, + "VC": { "flag": "๐Ÿ‡ป๐Ÿ‡จ", "name": "St. Vincent & Grenadines", "code": "1-784" }, "VE": { "flag": "๐Ÿ‡ป๐Ÿ‡ช", "name": "Venezuela", "code": "58" }, - "VG": { "flag": "๐Ÿ‡ป๐Ÿ‡ฌ", "name": "British Virgin Islands", "code": "+1-284" }, - "VI": { "flag": "๐Ÿ‡ป๐Ÿ‡ฎ", "name": "U.S. Virgin Islands", "code": "+1-340" }, + "VG": { "flag": "๐Ÿ‡ป๐Ÿ‡ฌ", "name": "British Virgin Islands", "code": "1-284" }, + "VI": { "flag": "๐Ÿ‡ป๐Ÿ‡ฎ", "name": "U.S. Virgin Islands", "code": "1-340" }, "VN": { "flag": "๐Ÿ‡ป๐Ÿ‡ณ", "name": "Vietnam", "code": "84" }, "VU": { "flag": "๐Ÿ‡ป๐Ÿ‡บ", "name": "Vanuatu", "code": "678" }, "WF": { "flag": "๐Ÿ‡ผ๐Ÿ‡ซ", "name": "Wallis & Futuna", "code": "681" }, diff --git a/src/Components/Assets/AssetConfigure.tsx b/src/Components/Assets/AssetConfigure.tsx index 7099f911463..fd431ae35ea 100644 --- a/src/Components/Assets/AssetConfigure.tsx +++ b/src/Components/Assets/AssetConfigure.tsx @@ -1,60 +1,32 @@ -import { useCallback, useState } from "react"; import Loading from "../Common/Loading"; -import { AssetData } from "./AssetTypes"; -import { statusType, useAbortableEffect } from "../../Common/utils"; -import { useDispatch } from "react-redux"; -import { getAsset } from "../../Redux/actions"; -import * as Notification from "../../Utils/Notifications.js"; import HL7Monitor from "./AssetType/HL7Monitor"; import ONVIFCamera from "./AssetType/ONVIFCamera"; import Page from "../Common/components/Page"; +import useQuery from "../../Utils/request/useQuery"; +import routes from "../../Redux/api"; interface AssetConfigureProps { assetId: string; facilityId: string; } -const AssetConfigure = (props: AssetConfigureProps) => { - const { assetId, facilityId } = props; - const [asset, setAsset] = useState(); - const [isLoading, setIsLoading] = useState(true); - const [assetType, setAssetType] = useState(""); - const dispatch = useDispatch(); +const AssetConfigure = ({ assetId, facilityId }: AssetConfigureProps) => { + const { + data: asset, + loading, + refetch, + } = useQuery(routes.getAsset, { pathParams: { external_id: assetId } }); - const fetchData = useCallback( - async (status: statusType) => { - setIsLoading(true); - const [assetData]: any = await Promise.all([dispatch(getAsset(assetId))]); - if (!status.aborted) { - setIsLoading(false); - if (!assetData.data) - Notification.Error({ - msg: "Something went wrong..!", - }); - else { - setAsset(assetData.data); - setAssetType(assetData.data.asset_class); - } - } - }, - [dispatch, assetId] - ); - - useAbortableEffect( - (status: statusType) => { - fetchData(status); - }, - [dispatch, fetchData] - ); - - if (isLoading) return ; + if (loading || !asset) { + return ; + } - if (assetType === "HL7MONITOR") { + if (asset.asset_class === "HL7MONITOR") { return ( { ); } - if (assetType === "VENTILATOR") { + if (asset.asset_class === "VENTILATOR") { return ( { }} backUrl={`/facility/${facilityId}/assets/${assetId}`} > - + refetch()} + /> ); }; diff --git a/src/Components/Assets/AssetManage.tsx b/src/Components/Assets/AssetManage.tsx index 143ca2add29..7b3d39314eb 100644 --- a/src/Components/Assets/AssetManage.tsx +++ b/src/Components/Assets/AssetManage.tsx @@ -26,16 +26,16 @@ import { UserRole, USER_TYPES } from "../../Common/constants"; import ConfirmDialog from "../Common/ConfirmDialog"; import RecordMeta from "../../CAREUI/display/RecordMeta"; import { useTranslation } from "react-i18next"; -const PageTitle = lazy(() => import("../Common/PageTitle")); const Loading = lazy(() => import("../Common/Loading")); import * as Notification from "../../Utils/Notifications.js"; -import AuthorizeFor, { NonReadOnlyUsers } from "../../Utils/AuthorizeFor"; +import { NonReadOnlyUsers } from "../../Utils/AuthorizeFor"; import Uptime from "../Common/Uptime"; import useAuthUser from "../../Common/hooks/useAuthUser"; import dayjs from "dayjs"; import RelativeDateUserMention from "../Common/RelativeDateUserMention"; import { AssetServiceEditModal } from "./AssetServiceEditModal"; import { warrantyAmcValidityChip } from "./AssetsList"; +import Page from "../Common/components/Page"; interface AssetManageProps { assetId: string; @@ -232,6 +232,7 @@ const AssetManage = (props: AssetManageProps) => { { setServiceEditData({ ...service, open: true }); @@ -241,6 +242,7 @@ const AssetManage = (props: AssetManageProps) => { {
{item.label}
-
+
{item.content || "--"}
@@ -330,18 +335,28 @@ const AssetManage = (props: AssetManageProps) => { }; return ( -
- + + + Export as JSON + + } + > { {asset?.name} - - - - Export as JSON - - +
+ + {assetClassProp.name} +
+
+
+ {asset?.description}
+ {asset?.asset_type === "INTERNAL" ? ( + + ) : ( + + )} {asset?.status === "ACTIVE" ? ( ) : ( @@ -398,7 +414,9 @@ const AssetManage = (props: AssetManageProps) => { )}
- {asset?.description} +
+ {asset?.description} +
{[ @@ -407,19 +425,6 @@ const AssetManage = (props: AssetManageProps) => { icon: "location-pin-alt", content: asset?.location_object.name, }, - { - label: "Asset Type", - icon: "apps", - content: - asset?.asset_type === "INTERNAL" - ? "Internal Asset" - : "External Asset", - }, - { - label: "Asset Class", - icon: assetClassProp.icon, - content: assetClassProp.name, - }, { label: "Asset QR Code ID", icon: "qrcode-scan", @@ -457,7 +462,6 @@ const AssetManage = (props: AssetManageProps) => { } id="configure-asset" data-testid="asset-configure-button" - authorizeFor={AuthorizeFor(["DistrictAdmin", "StateAdmin"])} > {t("configure")} @@ -518,7 +522,10 @@ const AssetManage = (props: AssetManageProps) => { asset?.asset_class && asset?.asset_class != AssetClass.NONE && }
Service History
-
+
@@ -545,7 +552,10 @@ const AssetManage = (props: AssetManageProps) => {
Transaction History
-
+
@@ -590,7 +600,7 @@ const AssetManage = (props: AssetManageProps) => { viewOnly={serviceEditData.viewOnly} /> )} - + ); }; diff --git a/src/Components/Assets/AssetServiceEditModal.tsx b/src/Components/Assets/AssetServiceEditModal.tsx index 75c9dd9fc60..66d44d11907 100644 --- a/src/Components/Assets/AssetServiceEditModal.tsx +++ b/src/Components/Assets/AssetServiceEditModal.tsx @@ -97,7 +97,10 @@ export const AssetServiceEditModal = (props: { {edit.edited_by.username}

-
+
@@ -124,19 +127,25 @@ export const AssetServiceEditModal = (props: {

Serviced On

-

+

{formatDate(editRecord.serviced_on)}

Notes

-

{editRecord.note || "-"}

+

+ {editRecord.note || "-"} +

)}
{ editRecord ? setEditRecord(undefined) : props.handleClose(); diff --git a/src/Components/Assets/AssetType/HL7Monitor.tsx b/src/Components/Assets/AssetType/HL7Monitor.tsx index b19190ed410..55f4d0c258e 100644 --- a/src/Components/Assets/AssetType/HL7Monitor.tsx +++ b/src/Components/Assets/AssetType/HL7Monitor.tsx @@ -15,6 +15,7 @@ import CareIcon from "../../../CAREUI/icons/CareIcon"; import TextFormField from "../../Form/FormFields/TextFormField"; import HL7PatientVitalsMonitor from "../../VitalsMonitor/HL7PatientVitalsMonitor"; import VentilatorPatientVitalsMonitor from "../../VitalsMonitor/VentilatorPatientVitalsMonitor"; +import useAuthUser from "../../../Common/hooks/useAuthUser"; interface HL7MonitorProps { assetId: string; @@ -31,7 +32,7 @@ const HL7Monitor = (props: HL7MonitorProps) => { const [isLoading, setIsLoading] = useState(true); const [localipAddress, setLocalIPAddress] = useState(""); const [ipadrdress_error, setIpAddress_error] = useState(""); - + const authUser = useAuthUser(); const dispatch = useDispatch(); useEffect(() => { @@ -87,40 +88,42 @@ const HL7Monitor = (props: HL7MonitorProps) => { return (
-
- -
-

Connection

-
- setMiddlewareHostname(e.value)} - errorClassName="hidden" - /> - setLocalIPAddress(e.value)} - required - error={ipadrdress_error} - /> - - - Save Configuration - -
- -
- {["HL7MONITOR"].includes(assetType) && ( - - + {["DistrictAdmin", "StateAdmin"].includes(authUser.user_type) && ( +
+ +
+

Connection

+
+ setMiddlewareHostname(e.value)} + errorClassName="hidden" + /> + setLocalIPAddress(e.value)} + required + error={ipadrdress_error} + /> + + + Save Configuration + +
+
- )} -
+ {["HL7MONITOR"].includes(assetType) && ( + + + + )} +
+ )} {assetType === "HL7MONITOR" && ( void; } -const ONVIFCamera = (props: ONVIFCameraProps) => { - const { assetId, facilityId, asset } = props; +const ONVIFCamera = ({ assetId, facilityId, asset, onUpdated }: Props) => { const [isLoading, setIsLoading] = useState(true); const [assetType, setAssetType] = useState(""); const [middlewareHostname, setMiddlewareHostname] = useState(""); @@ -42,9 +43,8 @@ const ONVIFCamera = (props: ONVIFCameraProps) => { const [refreshPresetsHash, setRefreshPresetsHash] = useState( Number(new Date()) ); - const [refreshHash, setRefreshHash] = useState(Number(new Date())); const dispatch = useDispatch(); - + const authUser = useAuthUser(); useEffect(() => { const fetchFacility = async () => { const res = await dispatch(getPermittedFacility(facilityId)); @@ -87,14 +87,10 @@ const ONVIFCamera = (props: ONVIFCameraProps) => { dispatch(partialUpdateAsset(assetId, data)) ); if (res?.status === 200) { - Notification.Success({ - msg: "Asset Configured Successfully", - }); - setRefreshHash(Number(new Date())); + Notification.Success({ msg: "Asset Configured Successfully" }); + onUpdated?.(); } else { - Notification.Error({ - msg: "Something went wrong..!", - }); + Notification.Error({ msg: "Something went wrong..!" }); } setLoadingSetConfiguration(false); } else { @@ -147,61 +143,62 @@ const ONVIFCamera = (props: ONVIFCameraProps) => { return (
-
-
- setMiddlewareHostname(value)} - /> - setCameraAddress(value)} - error={ipadrdress_error} - /> - setUsername(value)} - /> - setPassword(value)} - /> - setStreamUuid(value)} - /> -
-
- -
- + {["DistrictAdmin", "StateAdmin"].includes(authUser.user_type) && ( +
+
+ setMiddlewareHostname(value)} + /> + setCameraAddress(value)} + error={ipadrdress_error} + /> + setUsername(value)} + /> + setPassword(value)} + /> + setStreamUuid(value)} + /> +
+
+ +
+ + )} {assetType === "ONVIF" ? ( { + if (isCopied) { + const timeout = setTimeout(() => { + setIsCopied(false); + }, 2000); + return () => clearTimeout(timeout); + } + }, [isCopied]); + return (
{asset.manufacturer}
-
-
+
+
{Object.keys(details).map((key) => (
{key}
-
+
{details[key as keyof typeof details] || "--"} + {key === "Serial Number" && ( + + )}
))}
+
diff --git a/src/Components/CriticalCareRecording/NeurologicalMonitoring/CriticalCare__NeurologicalMonitoringEditor.res b/src/Components/CriticalCareRecording/NeurologicalMonitoring/CriticalCare__NeurologicalMonitoringEditor.res index f3f789e9dcd..35d137e0414 100644 --- a/src/Components/CriticalCareRecording/NeurologicalMonitoring/CriticalCare__NeurologicalMonitoringEditor.res +++ b/src/Components/CriticalCareRecording/NeurologicalMonitoring/CriticalCare__NeurologicalMonitoringEditor.res @@ -485,7 +485,7 @@ let make = (~updateCB, ~neurologicalMonitoring, ~id, ~consultationId) => {
{str("Glasgow Coma Scale")}
-
{str("Eye Open")}
+
{str("Eye Opening Response")}
{Js.Array.mapi( (x, i) => diff --git a/src/Components/CriticalCareRecording/types/CriticalCare__NeurologicalMonitoring.res b/src/Components/CriticalCareRecording/types/CriticalCare__NeurologicalMonitoring.res index 1e8804c7524..f42ca5c140d 100644 --- a/src/Components/CriticalCareRecording/types/CriticalCare__NeurologicalMonitoring.res +++ b/src/Components/CriticalCareRecording/types/CriticalCare__NeurologicalMonitoring.res @@ -45,25 +45,25 @@ let make = ( ~limbResponseLowerExtremityRight, ~limbResponseLowerExtremityLeft, ) => { - inPronePosition: inPronePosition, - consciousnessLevel: consciousnessLevel, - consciousnessLevelDetails: consciousnessLevelDetails, - leftPupilSize: leftPupilSize, - leftPupilSizeDetails: leftPupilSizeDetails, - leftPupilLightReaction: leftPupilLightReaction, - leftPupilLightReactionDetails: leftPupilLightReactionDetails, - rightPupilSize: rightPupilSize, - rightPupilSizeDetails: rightPupilSizeDetails, - rightPupilLightReaction: rightPupilLightReaction, - rightPupilLightReactionDetails: rightPupilLightReactionDetails, - glasgowEyeOpen: glasgowEyeOpen, - glasgowVerbalResponse: glasgowVerbalResponse, - glasgowMotorResponse: glasgowMotorResponse, - glasgowTotalCalculated: glasgowTotalCalculated, - limbResponseUpperExtremityRight: limbResponseUpperExtremityRight, - limbResponseUpperExtremityLeft: limbResponseUpperExtremityLeft, - limbResponseLowerExtremityRight: limbResponseLowerExtremityRight, - limbResponseLowerExtremityLeft: limbResponseLowerExtremityLeft, + inPronePosition, + consciousnessLevel, + consciousnessLevelDetails, + leftPupilSize, + leftPupilSizeDetails, + leftPupilLightReaction, + leftPupilLightReactionDetails, + rightPupilSize, + rightPupilSizeDetails, + rightPupilLightReaction, + rightPupilLightReactionDetails, + glasgowEyeOpen, + glasgowVerbalResponse, + glasgowMotorResponse, + glasgowTotalCalculated, + limbResponseUpperExtremityRight, + limbResponseUpperExtremityLeft, + limbResponseLowerExtremityRight, + limbResponseLowerExtremityLeft, } let makeConsciousnessLevel = consciousnessLevel => { @@ -173,8 +173,8 @@ let limpResponseToString = limpResponse => { let eyeOpenToString = eyeOpen => { switch eyeOpen { - | 1 => "1 - None" - | 2 => "2 - Pain" + | 1 => "1 - No Response" + | 2 => "2 - To Pain" | 3 => "3 - To Speech" | 4 => "4 - Spontaneous" | _ => "Unknown" @@ -183,23 +183,23 @@ let eyeOpenToString = eyeOpen => { let motorResposneToString = eyeOpen => { switch eyeOpen { - | 1 => "1 - None" - | 2 => "2 - Incomprehensible words/Moans to pain" + | 1 => "1 - No Response" + | 2 => "2 - Abnormal Extension" | 3 => "3 - Abnormal Flexion" - | 4 => "4 - Withdrawing" - | 5 => "5 - Localizing/Withdrawl to touch" - | 6 => "6 - Obeying/Normal Activity" + | 4 => "4 - Flexion/Withdrawal to pain" + | 5 => "5 - Moves to localized pain" + | 6 => "6 - Obeys commands/Normal Activity" | _ => "Unknown" } } let verbalResposneToString = eyeOpen => { switch eyeOpen { - | 1 => "1 - None" + | 1 => "1 - No Response" | 2 => "2 - Incomprehensible words/Moans to pain" | 3 => "3 - Inappropriate words/Cry to pain" | 4 => "4 - Confused/Irritable" - | 5 => "5 - Oriented/Coos/Babbies" + | 5 => "5 - Oriented to Time, Place and Person" | _ => "Unknown" } } diff --git a/src/Components/ExternalResult/FacilitiesSelectDialogue.tsx b/src/Components/ExternalResult/FacilitiesSelectDialogue.tsx index 763d92f2d1f..7239e0b912d 100644 --- a/src/Components/ExternalResult/FacilitiesSelectDialogue.tsx +++ b/src/Components/ExternalResult/FacilitiesSelectDialogue.tsx @@ -34,7 +34,7 @@ const FacilitiesSelectDialog = (props: Props) => { diff --git a/src/Components/ExternalResult/ResultList.tsx b/src/Components/ExternalResult/ResultList.tsx index 18de6f29134..74fbf8430b0 100644 --- a/src/Components/ExternalResult/ResultList.tsx +++ b/src/Components/ExternalResult/ResultList.tsx @@ -6,7 +6,7 @@ import { externalResultList } from "../../Redux/actions"; import ListFilter from "./ListFilter"; import FacilitiesSelectDialogue from "./FacilitiesSelectDialogue"; import { FacilityModel } from "../Facility/models"; -import parsePhoneNumberFromString from "libphonenumber-js"; +import { parsePhoneNumber } from "../../Utils/utils"; import SearchInput from "../Form/SearchInput"; import useFilters from "../../Common/hooks/useFilters"; import CareIcon from "../../CAREUI/icons/CareIcon"; @@ -65,7 +65,7 @@ export default function ResultList() { page: qParams.page || 1, name: qParams.name || "", mobile_number: qParams.mobile_number - ? parsePhoneNumberFromString(qParams.mobile_number)?.format("E.164") + ? parsePhoneNumber(qParams.mobile_number) ?? "" : "", wards: qParams.wards || undefined, local_bodies: qParams.local_bodies || undefined, diff --git a/src/Components/Facility/AssetCreate.tsx b/src/Components/Facility/AssetCreate.tsx index 1e6eead5242..156d738857a 100644 --- a/src/Components/Facility/AssetCreate.tsx +++ b/src/Components/Facility/AssetCreate.tsx @@ -30,13 +30,12 @@ import TextAreaFormField from "../Form/FormFields/TextAreaFormField"; import TextFormField from "../Form/FormFields/TextFormField"; import { navigate } from "raviger"; -import { parsePhoneNumberFromString } from "libphonenumber-js"; import { parseQueryParams } from "../../Utils/primitives"; import useAppHistory from "../../Common/hooks/useAppHistory"; import { useDispatch } from "react-redux"; import useVisibility from "../../Utils/useVisibility"; import { validateEmailAddress } from "../../Common/validation"; -import { dateQueryString } from "../../Utils/utils.js"; +import { dateQueryString, parsePhoneNumber } from "../../Utils/utils.js"; import dayjs from "../../Utils/dayjs"; import DateInputV2 from "../Common/DateInputV2.js"; @@ -341,7 +340,7 @@ const AssetCreate = (props: AssetProps) => { support_email: support_email, support_phone: support_phone.startsWith("1800") ? support_phone - : parsePhoneNumberFromString(support_phone)?.format("E.164"), + : parsePhoneNumber(support_phone), qr_code_id: qrCodeId !== "" ? qrCodeId : null, manufacturer: manufacturer, warranty_amc_end_of_validity: warranty_amc_end_of_validity diff --git a/src/Components/Facility/ConsultationDetails/ConsultationUpdatesTab.tsx b/src/Components/Facility/ConsultationDetails/ConsultationUpdatesTab.tsx index 705b3d9479a..1b89f3980e1 100644 --- a/src/Components/Facility/ConsultationDetails/ConsultationUpdatesTab.tsx +++ b/src/Components/Facility/ConsultationDetails/ConsultationUpdatesTab.tsx @@ -10,7 +10,7 @@ import useVitalsAspectRatioConfig from "../../VitalsMonitor/useVitalsAspectRatio import { DISCHARGE_REASONS, SYMPTOM_CHOICES } from "../../../Common/constants"; import PrescriptionsTable from "../../Medicine/PrescriptionsTable"; import Chip from "../../../CAREUI/display/Chip"; -import { formatDate, formatDateTime } from "../../../Utils/utils"; +import { formatAge, formatDate, formatDateTime } from "../../../Utils/utils"; import ReadMore from "../../Common/components/Readmore"; import { DailyRoundsList } from "../Consultations/DailyRoundsList"; @@ -621,7 +621,12 @@ export const ConsultationUpdatesTab = (props: ConsultationTabProps) => {
Age {" - "} - {props.patientData.age ?? "-"} + {props.patientData.age !== undefined // 0 is a valid age, so we need to check for undefined + ? formatAge( + props.patientData.age, + props.patientData.date_of_birth + ) + : "-"}
diff --git a/src/Components/Facility/ConsultationDetails/index.tsx b/src/Components/Facility/ConsultationDetails/index.tsx index affdb2756cd..403f1752704 100644 --- a/src/Components/Facility/ConsultationDetails/index.tsx +++ b/src/Components/Facility/ConsultationDetails/index.tsx @@ -5,10 +5,14 @@ import { SYMPTOM_CHOICES, } from "../../../Common/constants"; import { ConsultationModel, ICD11DiagnosisModel } from "../models"; -import { getConsultation, getPatient } from "../../../Redux/actions"; +import { + getConsultation, + getPatient, + listShiftRequests, +} from "../../../Redux/actions"; import { statusType, useAbortableEffect } from "../../../Common/utils"; import { lazy, useCallback, useState } from "react"; - +import ToolTip from "../../Common/utils/Tooltip"; import ButtonV2 from "../../Common/components/ButtonV2"; import CareIcon from "../../../CAREUI/icons/CareIcon"; import DischargeModal from "../DischargeModal"; @@ -23,7 +27,7 @@ import { navigate } from "raviger"; import { useDispatch } from "react-redux"; import { useQueryParams } from "raviger"; import { useTranslation } from "react-i18next"; -import { triggerGoal } from "../../Common/Plausible"; +import { triggerGoal } from "../../../Integrations/Plausible"; import useAuthUser from "../../../Common/hooks/useAuthUser"; import { ConsultationUpdatesTab } from "./ConsultationUpdatesTab"; import { ConsultationABGTab } from "./ConsultationABGTab"; @@ -50,10 +54,26 @@ export interface ConsultationTabProps { patientData: PatientModel; } +const TABS = { + UPDATES: ConsultationUpdatesTab, + FEED: ConsultationFeedTab, + SUMMARY: ConsultationSummaryTab, + MEDICINES: ConsultationMedicinesTab, + FILES: ConsultationFilesTab, + INVESTIGATIONS: ConsultationInvestigationsTab, + ABG: ConsultationABGTab, + NURSING: ConsultationNursingTab, + NEUROLOGICAL_MONITORING: ConsultationNeurologicalMonitoringTab, + VENTILATOR: ConsultationVentilatorTab, + NUTRITION: ConsultationNursingTab, + PRESSURE_SORE: ConsultationPressureSoreTab, + DIALYSIS: ConsultationDialysisTab, +}; + export const ConsultationDetails = (props: any) => { const { t } = useTranslation(); const { facilityId, patientId, consultationId } = props; - const tab = props.tab.toUpperCase(); + const tab = props.tab.toUpperCase() as keyof typeof TABS; const dispatch: any = useDispatch(); const [isLoading, setIsLoading] = useState(false); const [showDoctors, setShowDoctors] = useState(false); @@ -63,6 +83,7 @@ export const ConsultationDetails = (props: any) => { {} as ConsultationModel ); const [patientData, setPatientData] = useState({}); + const [activeShiftingData, setActiveShiftingData] = useState>([]); const [openDischargeSummaryDialog, setOpenDischargeSummaryDialog] = useState(false); const [openDischargeDialog, setOpenDischargeDialog] = useState(false); @@ -124,6 +145,15 @@ export const ConsultationDetails = (props: any) => { }; setPatientData(data); } + + // Get shifting data + const shiftingRes = await dispatch( + listShiftRequests({ patient: id }, "shift-list-call") + ); + if (shiftingRes?.data?.results) { + const data = shiftingRes.data.results; + setActiveShiftingData(data); + } } else { navigate("/not-found"); } @@ -142,22 +172,6 @@ export const ConsultationDetails = (props: any) => { }); }, []); - const TABS = { - UPDATES: ConsultationUpdatesTab, - FEED: ConsultationFeedTab, - SUMMARY: ConsultationSummaryTab, - MEDICINES: ConsultationMedicinesTab, - FILES: ConsultationFilesTab, - INVESTIGATIONS: ConsultationInvestigationsTab, - ABG: ConsultationABGTab, - NURSING: ConsultationNursingTab, - NEUROLOGICAL_MONITORING: ConsultationNeurologicalMonitoringTab, - VENTILATOR: ConsultationVentilatorTab, - NUTRITION: ConsultationNursingTab, - PRESSURE_SORE: ConsultationPressureSoreTab, - DIALYSIS: ConsultationDialysisTab, - }; - const consultationTabProps: ConsultationTabProps = { consultationId, facilityId, @@ -168,6 +182,19 @@ export const ConsultationDetails = (props: any) => { const SelectedTab = TABS[tab]; + const hasActiveShiftingRequest = () => { + if (activeShiftingData.length > 0) { + return [ + "PENDING", + "APPROVED", + "DESTINATION APPROVED", + "PATIENT TO BE PICKED UP", + ].includes(activeShiftingData[activeShiftingData.length - 1].status); + } + + return false; + }; + if (isLoading) { return ; } @@ -191,10 +218,20 @@ export const ConsultationDetails = (props: any) => { return diagnoses.length ? (

{label}

- - {diagnoses.slice(0, !showMore ? nshow : undefined).map((diagnosis) => ( -

{diagnosis.label}

- ))} + {diagnoses.slice(0, !showMore ? nshow : undefined).map((diagnosis) => + diagnosis.id === consultationData.icd11_principal_diagnosis ? ( +
+

{diagnosis.label}

+
+ + + +
+
+ ) : ( +

{diagnosis.label}

+ ) + )} {diagnoses.length > nshow && ( <> {!showMore ? ( @@ -255,17 +292,33 @@ export const ConsultationDetails = (props: any) => {
{!consultationData.discharge_date && (
- - navigate( - `/facility/${patientData.facility}/patient/${patientData.id}/shift/new` - ) - } - className="btn btn-primary m-1 w-full hover:text-white" - > - - Shift Patient - + {hasActiveShiftingRequest() ? ( + + navigate( + `/shifting/${ + activeShiftingData[activeShiftingData.length - 1].id + }` + ) + } + className="btn btn-primary m-1 w-full hover:text-white" + > + + Track Shifting + + ) : ( + + navigate( + `/facility/${patientData.facility}/patient/${patientData.id}/shift/new` + ) + } + className="btn btn-primary m-1 w-full hover:text-white" + > + + Shift Patient + + )}
)*/} - {consultationData.icd11_principal_diagnosis && ( - - d.id === consultationData.icd11_principal_diagnosis - )!, - ]} - /> - )} - { { consultationData.deprecated_verified_by) && (
- Verified By:{" "} + Treating Physician:{" "} {consultationData.verified_by_object ? `${consultationData.verified_by_object.first_name} ${consultationData.verified_by_object.last_name}` diff --git a/src/Components/Facility/ConsultationForm.tsx b/src/Components/Facility/ConsultationForm.tsx index 49028a62953..01a5f02b7b1 100644 --- a/src/Components/Facility/ConsultationForm.tsx +++ b/src/Components/Facility/ConsultationForm.tsx @@ -97,7 +97,7 @@ type FormDetails = { procedure: ProcedureType[]; investigation: InvestigationType[]; is_telemedicine: BooleanStrings; - action?: string; + action?: number; assigned_to: string; assigned_to_object: UserModel | null; special_instruction: string; @@ -123,7 +123,7 @@ const initForm: FormDetails = { facility: "", admitted: "false", admitted_to: "", - category: "Comfort", + category: "", admission_date: new Date(), discharge_date: null, referred_to: "", @@ -143,7 +143,7 @@ const initForm: FormDetails = { procedure: [], investigation: [], is_telemedicine: "false", - action: "NO_ACTION", + action: 10, assigned_to: "", assigned_to_object: null, special_instruction: "", @@ -279,15 +279,14 @@ export const ConsultationForm = (props: any) => { setIsLoading(true); const res = await dispatchAction(getPatient({ id: patientId })); if (res.data) { - setPatientName(res.data.name); - setFacilityName(res.data.facility_object.name); if (isUpdate) { - const form = { ...state.form }; - form.action = TELEMEDICINE_ACTIONS.find( - (a) => a.id === res.data.action - )?.text; - dispatch({ type: "set_form", form }); + dispatch({ + type: "set_form", + form: { ...state.form, action: res.data.action }, + }); } + setPatientName(res.data.name); + setFacilityName(res.data.facility_object.name); } } else { setPatientName(""); @@ -302,6 +301,49 @@ export const ConsultationForm = (props: any) => { !!state.form.symptoms.length && !state.form.symptoms.includes(1); const isOtherSymptomsSelected = state.form.symptoms.includes(9); + const handleFormFieldChange: FieldChangeEventHandler = (event) => { + if (event.name === "consultation_status" && event.value === "1") { + dispatch({ + type: "set_form", + form: { + ...state.form, + consultation_status: 1, + symptoms: [1], + symptoms_onset_date: new Date(), + category: "Critical", + suggestion: "DD", + }, + }); + } else if (event.name === "suggestion" && event.value === "DD") { + dispatch({ + type: "set_form", + form: { + ...state.form, + suggestion: "DD", + consultation_notes: "Patient declared dead", + verified_by: "Declared Dead", + }, + }); + } else if ( + event.name === "icd11_diagnoses_object" || + event.name === "icd11_provisional_diagnoses_object" + ) { + dispatch({ + type: "set_form", + form: { + ...state.form, + [event.name]: event.value, + icd11_principal_diagnosis: undefined, + }, + }); + } else { + dispatch({ + type: "set_form", + form: { ...state.form, [event.name]: event.value }, + }); + } + }; + const fetchData = useCallback( async (status: statusType) => { if (!patientId) setIsLoading(true); @@ -331,8 +373,8 @@ export const ConsultationForm = (props: any) => { admitted_to: res.data.admitted_to ? res.data.admitted_to : "", category: res.data.category ? PATIENT_CATEGORIES.find((i) => i.text === res.data.category) - ?.id ?? "Comfort" - : "Comfort", + ?.id ?? "" + : "", patient_no: res.data.patient_no ?? "", OPconsultation: res.data.consultation_notes, is_telemedicine: `${res.data.is_telemedicine}`, @@ -352,7 +394,7 @@ export const ConsultationForm = (props: any) => { death_confirmed_doctor: res.data?.death_confirmed_doctor || "", InvestigationAdvice: res.data.investigation, }; - dispatch({ type: "set_form", form: formData }); + dispatch({ type: "set_form", form: { ...state.form, ...formData } }); setBed(formData.bed); if (res.data.last_daily_round) { @@ -364,7 +406,7 @@ export const ConsultationForm = (props: any) => { setIsLoading(false); } }, - [dispatchAction, id] + [dispatchAction, id, patientName, patientId] ); useAbortableEffect( @@ -539,7 +581,7 @@ export const ConsultationForm = (props: any) => { case "verified_by": { if (state.form.suggestion !== "DD" && !state.form[field]) { - errors[field] = "Please fill verified by"; + errors[field] = "Please fill treating physician"; invalidForm = true; break; } @@ -745,49 +787,6 @@ export const ConsultationForm = (props: any) => { } }; - const handleFormFieldChange: FieldChangeEventHandler = (event) => { - if (event.name === "consultation_status" && event.value === "1") { - dispatch({ - type: "set_form", - form: { - ...state.form, - consultation_status: 1, - symptoms: [1], - symptoms_onset_date: new Date(), - category: "Critical", - suggestion: "DD", - }, - }); - } else if (event.name === "suggestion" && event.value === "DD") { - dispatch({ - type: "set_form", - form: { - ...state.form, - suggestion: "DD", - consultation_notes: "Patient declared dead", - verified_by: "Declared Dead", - }, - }); - } else if ( - event.name === "icd11_diagnoses_object" || - event.name === "icd11_provisional_diagnoses_object" - ) { - dispatch({ - type: "set_form", - form: { - ...state.form, - [event.name]: event.value, - icd11_principal_diagnosis: undefined, - }, - }); - } else { - dispatch({ - type: "set_form", - form: { ...state.form, [event.name]: event.value }, - }); - } - }; - const handleDoctorSelect = (event: FieldChangeEvent) => { if (event.value?.id) { dispatch({ @@ -1321,7 +1320,7 @@ export const ConsultationForm = (props: any) => { > {
option.desc} - optionValue={(option) => option.text} + optionDescription={() => ""} />
diff --git a/src/Components/Facility/Consultations/Feed.tsx b/src/Components/Facility/Consultations/Feed.tsx index 84b3e62ef9e..75ab72d168c 100644 --- a/src/Components/Facility/Consultations/Feed.tsx +++ b/src/Components/Facility/Consultations/Feed.tsx @@ -30,7 +30,7 @@ import { useDispatch } from "react-redux"; import { useHLSPLayer } from "../../../Common/hooks/useHLSPlayer"; import useKeyboardShortcut from "use-keyboard-shortcut"; import useFullscreen from "../../../Common/hooks/useFullscreen.js"; -import { triggerGoal } from "../../Common/Plausible.js"; +import { triggerGoal } from "../../../Integrations/Plausible.js"; import useAuthUser from "../../../Common/hooks/useAuthUser.js"; interface IFeedProps { diff --git a/src/Components/Facility/Consultations/NeurologicalTables.tsx b/src/Components/Facility/Consultations/NeurologicalTables.tsx index 79a0675a708..89e9d598604 100644 --- a/src/Components/Facility/Consultations/NeurologicalTables.tsx +++ b/src/Components/Facility/Consultations/NeurologicalTables.tsx @@ -411,7 +411,9 @@ export const NeurologicalTable = (props: any) => {
Scale Description
-
Eye Open
+
+ Eye Opening Response +
{EYE_OPEN_SCALE.map((x: any) => (
(); const [isCreateClaimLoading, setIsCreateClaimLoading] = useState(false); const [isSendingDischargeApi, setIsSendingDischargeApi] = useState(false); - const [facility, setFacility] = useState({ id: 0, name: "" }); // for referred to external + const [facility, setFacility] = useState(); const [errors, setErrors] = useState({}); const fetchLatestClaim = useCallback(async () => { @@ -186,12 +186,13 @@ const DischargeModal = ({ const prescriptionActions = PrescriptionActions(consultationData.id ?? ""); const handleFacilitySelect = (selected: FacilityModel) => { - setFacility(selected ? selected : facility); - const { id, name } = selected; + setFacility(selected); + const { id, name } = selected || {}; const isExternal = id === -1; setPreDischargeForm((prev) => ({ ...prev, - ...(isExternal ? { referred_to_external: name } : { referred_to: id }), + referred_to: isExternal ? null : id, + referred_to_external: isExternal ? name : null, })); }; @@ -238,8 +239,8 @@ const DischargeModal = ({ handleFacilitySelect(selected as FacilityModel) } selected={facility} - showAll={true} - freeText={true} + showAll + freeText multiple={false} errors={errors?.referred_to} className="mb-4" diff --git a/src/Components/Facility/FacilityCard.tsx b/src/Components/Facility/FacilityCard.tsx index e66fc4f3cc2..43e515cec93 100644 --- a/src/Components/Facility/FacilityCard.tsx +++ b/src/Components/Facility/FacilityCard.tsx @@ -9,7 +9,7 @@ import ButtonV2, { Cancel, Submit } from "../Common/components/ButtonV2"; import * as Notification from "../../Utils/Notifications.js"; import Chip from "../../CAREUI/display/Chip"; import CareIcon from "../../CAREUI/icons/CareIcon"; -import { parsePhoneNumber } from "libphonenumber-js"; +import { formatPhoneNumber, parsePhoneNumber } from "../../Utils/utils"; import DialogModal from "../Common/Dialog"; import TextAreaFormField from "../Form/FormFields/TextAreaFormField"; import useConfig from "../../Common/hooks/useConfig"; @@ -147,10 +147,9 @@ export const FacilityCard = (props: { facility: any; userType: any }) => { href={`tel:${facility.phone_number}`} className="text-sm font-semibold tracking-wider" > - {parsePhoneNumber( - facility.phone_number as string, - "IN" - ).formatInternational() || "-"} + {formatPhoneNumber( + parsePhoneNumber(facility.phone_number as string) ?? "-" + )}
diff --git a/src/Components/Facility/FacilityCreate.tsx b/src/Components/Facility/FacilityCreate.tsx index d1243daaf1d..e742caefb82 100644 --- a/src/Components/Facility/FacilityCreate.tsx +++ b/src/Components/Facility/FacilityCreate.tsx @@ -26,7 +26,11 @@ import { listDoctor, updateFacility, } from "../../Redux/actions"; -import { getPincodeDetails, includesIgnoreCase } from "../../Utils/utils"; +import { + getPincodeDetails, + includesIgnoreCase, + parsePhoneNumber, +} from "../../Utils/utils"; import { phonePreg, validateLatitude, @@ -51,11 +55,11 @@ import TextAreaFormField from "../Form/FormFields/TextAreaFormField"; import TextFormField from "../Form/FormFields/TextFormField"; import { navigate } from "raviger"; -import { parsePhoneNumberFromString } from "libphonenumber-js"; import useAppHistory from "../../Common/hooks/useAppHistory"; import useConfig from "../../Common/hooks/useConfig"; import { useDispatch } from "react-redux"; import { useTranslation } from "react-i18next"; +import { PhoneNumberValidator } from "../Form/FieldValidators.js"; const Loading = lazy(() => import("../Common/Loading")); @@ -434,11 +438,11 @@ export const FacilityCreate = (props: FacilityProps) => { return; case "phone_number": // eslint-disable-next-line no-case-declarations - const phoneNumber = parsePhoneNumberFromString(state.form[field]); + const phoneNumber = state.form[field]; if ( - !state.form[field] || - !phoneNumber?.isPossible() || - !phonePreg(String(phoneNumber?.number)) + !phoneNumber || + !PhoneNumberValidator()(phoneNumber) === undefined || + !phonePreg(phoneNumber) ) { errors[field] = t("invalid_phone_number"); invalidForm = true; @@ -488,9 +492,7 @@ export const FacilityCreate = (props: FacilityProps) => { kasp_empanelled: JSON.parse(state.form.kasp_empanelled), latitude: state.form.latitude || null, longitude: state.form.longitude || null, - phone_number: parsePhoneNumberFromString( - state.form.phone_number - )?.format("E.164"), + phone_number: parsePhoneNumber(state.form.phone_number), oxygen_capacity: state.form.oxygen_capacity ? state.form.oxygen_capacity : 0, diff --git a/src/Components/Facility/FacilityHome.tsx b/src/Components/Facility/FacilityHome.tsx index 81e7fa4906f..15317b1b56c 100644 --- a/src/Components/Facility/FacilityHome.tsx +++ b/src/Components/Facility/FacilityHome.tsx @@ -575,6 +575,7 @@ export const FacilityHome = (props: any) => { Resource Request navigate(`/facility/${facilityId}/assets/new`)} authorizeFor={NonReadOnlyUsers} icon={} @@ -582,12 +583,14 @@ export const FacilityHome = (props: any) => { Create Asset navigate(`/assets?facility=${facilityId}`)} icon={} > View Assets navigate(`/facility/${facilityId}/users`)} icon={} > diff --git a/src/Components/Facility/Investigations/Reports/ReportTable.tsx b/src/Components/Facility/Investigations/Reports/ReportTable.tsx index 0826ff904fc..1f20ec94180 100644 --- a/src/Components/Facility/Investigations/Reports/ReportTable.tsx +++ b/src/Components/Facility/Investigations/Reports/ReportTable.tsx @@ -2,7 +2,7 @@ import { getColorIndex, rowColor, transformData } from "./utils"; import ButtonV2 from "../../../Common/components/ButtonV2"; import { InvestigationResponse } from "./types"; -import { formatDateTime } from "../../../../Utils/utils"; +import { formatAge, formatDateTime } from "../../../../Utils/utils"; import { FC } from "react"; const ReportRow = ({ data, name, min, max }: any) => { @@ -53,6 +53,7 @@ interface ReportTableProps { patientDetails?: { name: string; age: number; + date_of_birth: string; hospitalName: string; }; investigationData: InvestigationResponse; @@ -83,7 +84,14 @@ const ReportTable: FC = ({ {patientDetails && (

Name: {patientDetails.name}

-

Age: {patientDetails.age}

+

+ Age:{" "} + {formatAge( + patientDetails.age, + patientDetails.date_of_birth, + true + )} +

Hospital: {patientDetails.hospitalName}

)} diff --git a/src/Components/Facility/Investigations/Reports/index.tsx b/src/Components/Facility/Investigations/Reports/index.tsx index c4c69bc9169..ab5380c62ca 100644 --- a/src/Components/Facility/Investigations/Reports/index.tsx +++ b/src/Components/Facility/Investigations/Reports/index.tsx @@ -100,8 +100,9 @@ const InvestigationReports = ({ id }: any) => { const [patientDetails, setPatientDetails] = useState<{ name: string; age: number; + date_of_birth: string; hospitalName: string; - }>({ name: "", age: -1, hospitalName: "" }); + }>({ name: "", age: -1, date_of_birth: "", hospitalName: "" }); const [state, dispatch] = useReducer( investigationReportsReducer, initialState @@ -220,6 +221,7 @@ const InvestigationReports = ({ id }: any) => { setPatientDetails({ name: res.data.name, age: res.data.age, + date_of_birth: res.data.date_of_birth, hospitalName: res.data.facility_object.name, }); } @@ -227,6 +229,7 @@ const InvestigationReports = ({ id }: any) => { setPatientDetails({ name: "", age: -1, + date_of_birth: "", hospitalName: "", }); } diff --git a/src/Components/Facility/LegacyMonitorCard.tsx b/src/Components/Facility/LegacyMonitorCard.tsx index 9f9fb8f1ea0..61bff3d607b 100644 --- a/src/Components/Facility/LegacyMonitorCard.tsx +++ b/src/Components/Facility/LegacyMonitorCard.tsx @@ -4,6 +4,7 @@ import CareIcon from "../../CAREUI/icons/CareIcon"; import { PatientModel } from "../Patient/models"; import LegacyPatientVitalsCard from "../Patient/LegacyPatientVitalsCard"; import { AssetLocationObject } from "../Assets/AssetTypes"; +import { formatAge } from "../../Utils/utils"; interface MonitorCardProps { facilityId: string; @@ -28,7 +29,7 @@ export const LegacyMonitorCard = ({ {patient.name} - {patient.age}y |{" "} + {formatAge(patient.age, patient.date_of_birth)} |{" "} {GENDER_TYPES.find((g) => g.id === patient.gender)?.icon} diff --git a/src/Components/Facility/TreatmentSummary.tsx b/src/Components/Facility/TreatmentSummary.tsx index dbca307d38b..3fbd80ac8b2 100644 --- a/src/Components/Facility/TreatmentSummary.tsx +++ b/src/Components/Facility/TreatmentSummary.tsx @@ -10,7 +10,7 @@ import { statusType, useAbortableEffect } from "../../Common/utils"; import { PatientModel } from "../Patient/models"; import { GENDER_TYPES } from "../../Common/constants"; -import { formatDate, formatDateTime } from "../../Utils/utils"; +import { formatAge, formatDate, formatDateTime } from "../../Utils/utils"; const Loading = lazy(() => import("../Common/Loading")); const TreatmentSummary = (props: any) => { @@ -132,7 +132,8 @@ const TreatmentSummary = (props: any) => {
- Age : {patientData.age} + Age :{" "} + {formatAge(patientData.age, patientData.date_of_birth, true)}
Date of admission : diff --git a/src/Components/Facility/models.tsx b/src/Components/Facility/models.tsx index cc34140e7a9..de2c6af698a 100644 --- a/src/Components/Facility/models.tsx +++ b/src/Components/Facility/models.tsx @@ -223,5 +223,4 @@ export interface CurrentBed { export type ICD11DiagnosisModel = { id: string; label: string; - parentId: string | null; }; diff --git a/src/Components/Form/AutoCompleteAsync.tsx b/src/Components/Form/AutoCompleteAsync.tsx index d06067af957..5f33c6388c5 100644 --- a/src/Components/Form/AutoCompleteAsync.tsx +++ b/src/Components/Form/AutoCompleteAsync.tsx @@ -7,6 +7,7 @@ import { MultiSelectOptionChip, dropdownOptionClassNames, } from "./MultiSelectMenuV2"; +import { useTranslation } from "react-i18next"; interface Props { name?: string; @@ -23,6 +24,7 @@ interface Props { placeholder?: string; disabled?: boolean; error?: string; + required?: boolean; onBlur?: () => void; onFocus?: () => void; } @@ -42,11 +44,13 @@ const AutoCompleteAsync = (props: Props) => { className = "", placeholder, disabled = false, + required = false, error, } = props; const [data, setData] = useState([]); const [query, setQuery] = useState(""); const [loading, setLoading] = useState(false); + const { t } = useTranslation(); const hasSelection = (!multiple && selected) || (multiple && selected?.length > 0); @@ -86,9 +90,7 @@ const AutoCompleteAsync = (props: Props) => { : placeholder || "Start typing to search..." } displayValue={() => - hasSelection && !multiple - ? optionLabel && optionLabel(selected) - : "" + hasSelection && !multiple ? optionLabel?.(selected) : "" } onChange={({ target }) => setQuery(target.value)} onFocus={props.onFocus} @@ -100,6 +102,20 @@ const AutoCompleteAsync = (props: Props) => { />
+ {hasSelection && !loading && !required && ( +
+ { + e.preventDefault(); + onChange(null); + }} + /> + + {t("clear_selection")} + +
+ )} {loading ? ( ) : ( diff --git a/src/Components/Form/FormFields/Autocomplete.tsx b/src/Components/Form/FormFields/Autocomplete.tsx index dcf3ab5e8b6..c4b3ea3686e 100644 --- a/src/Components/Form/FormFields/Autocomplete.tsx +++ b/src/Components/Form/FormFields/Autocomplete.tsx @@ -157,6 +157,7 @@ export const Autocomplete = (props: AutocompleteProps) => { placeholder={props.placeholder ?? "Select"} displayValue={(value: any) => value?.label || ""} onChange={(event) => setQuery(event.target.value.toLowerCase())} + onBlur={() => value && setQuery("")} autoComplete="off" /> @@ -164,7 +165,7 @@ export const Autocomplete = (props: AutocompleteProps) => { {value?.icon} {value && !props.isLoading && !props.required && ( -
+
{ diff --git a/src/Components/Form/FormFields/PhoneNumberFormField.tsx b/src/Components/Form/FormFields/PhoneNumberFormField.tsx index 64c93c51500..6aa70f801de 100644 --- a/src/Components/Form/FormFields/PhoneNumberFormField.tsx +++ b/src/Components/Form/FormFields/PhoneNumberFormField.tsx @@ -1,12 +1,13 @@ import { FormFieldBaseProps, useFormFieldPropsResolver } from "./Utils"; import FormField from "./FormField"; +import { useEffect, useMemo, useState } from "react"; import { - AsYouType, - isValidPhoneNumber, + classNames, parsePhoneNumber, -} from "libphonenumber-js"; -import { useMemo, useState } from "react"; -import { classNames } from "../../../Utils/utils"; + formatPhoneNumber as formatPhoneNumberUtil, + getCountryCode, + CountryData, +} from "../../../Utils/utils"; import phoneCodesJson from "../../../Common/static/countryPhoneAndFlags.json"; import { FieldError, @@ -15,12 +16,6 @@ import { } from "../FieldValidators"; import CareIcon from "../../../CAREUI/icons/CareIcon"; -interface CountryData { - flag: string; - name: string; - code: string; -} - const phoneCodes: Record = phoneCodesJson; interface Props extends FormFieldBaseProps { @@ -39,21 +34,6 @@ export default function PhoneNumberFormField(props: Props) { [props.types] ); - const asYouType = useMemo(() => { - const asYouType = new AsYouType(); - - asYouType.reset(); - - if (field.value) { - asYouType.input(field.value); - } else { - asYouType.input("+91"); - field.handleChange(asYouType.getNumberValue()); - } - - return asYouType; - }, []); - const validate = useMemo( () => (value: string | undefined, event: "blur" | "change") => { if (!value || props.disableValidation) { @@ -73,9 +53,9 @@ export default function PhoneNumberFormField(props: Props) { const setValue = (value: string) => { value = value.replaceAll(/[^0-9+]/g, ""); - - asYouType.reset(); - asYouType.input(value); + if (value.length > 12 && value.startsWith("+910")) { + value = "+91" + value.slice(4); + } const error = validate(value, "change"); field.handleChange(value); @@ -83,6 +63,8 @@ export default function PhoneNumberFormField(props: Props) { setError(error); }; + useEffect(() => setValue(field.value || "+91"), []); + return ( setValue(e.target.value)} disabled={field.disabled} onBlur={() => setError(validate(field.value, "blur"))} @@ -122,7 +104,7 @@ export default function PhoneNumberFormField(props: Props) { autoComplete="country" className="cui-input-base h-full border-0 bg-transparent pl-2 pr-8 text-end font-medium tracking-wider text-gray-700 focus:ring-2 focus:ring-inset" value={ - asYouType.getCountry() ?? + getCountryCode(field.value) ?? (field.value?.startsWith("1800") ? "1800" : "Other") } onChange={(e) => { @@ -176,15 +158,15 @@ const conditionPhoneCode = (code: string) => { return code.startsWith("+") ? code : "+" + code; }; -const formatPhoneNumber = (value: string) => { +const formatPhoneNumber = (value: string, types: PhoneNumberType[]) => { if (value === undefined || value === null) { return "+91 "; } - if (!isValidPhoneNumber(value)) { + if (PhoneNumberValidator(types)(value) !== undefined || value.length < 13) { return value; } const phoneNumber = parsePhoneNumber(value); - return phoneNumber.formatInternational(); + return phoneNumber ? formatPhoneNumberUtil(phoneNumber) : value; }; diff --git a/src/Components/Medicine/MedibaseAutocompleteFormField.tsx b/src/Components/Medicine/MedibaseAutocompleteFormField.tsx index 337546691f9..2b5612f6f32 100644 --- a/src/Components/Medicine/MedibaseAutocompleteFormField.tsx +++ b/src/Components/Medicine/MedibaseAutocompleteFormField.tsx @@ -49,7 +49,7 @@ export default function MedibaseAutocompleteFormField( value={field.value} required onChange={field.handleChange} - options={options(field.value && [field.value])} + options={options(field.value && !query && [field.value])} optionLabel={(option) => option.name.toUpperCase()} optionDescription={(option) => } optionValue={(option) => option} diff --git a/src/Components/Medicine/PrescriptionAdministrationsTable.tsx b/src/Components/Medicine/PrescriptionAdministrationsTable.tsx index 81282126d7c..470caa1042b 100644 --- a/src/Components/Medicine/PrescriptionAdministrationsTable.tsx +++ b/src/Components/Medicine/PrescriptionAdministrationsTable.tsx @@ -47,6 +47,10 @@ export default function PrescriptionAdministrationsTable({ const { t } = useTranslation(); const [state, setState] = useState(); + + const [showDiscontinued, setShowDiscontinued] = useState(false); + const [discontinuedCount, setDiscontinuedCount] = useState(); + const pagination = useRangePagination({ bounds: state?.administrationsTimeBounds ?? { start: new Date(), @@ -54,7 +58,8 @@ export default function PrescriptionAdministrationsTable({ }, perPage: 24 * 60 * 60 * 1000, slots: 24, - defaultEnd: true, + snapToLatest: true, + reverse: true, }); const [showBulkAdminister, setShowBulkAdminister] = useState(false); @@ -64,8 +69,13 @@ export default function PrescriptionAdministrationsTable({ ); const refetch = useCallback(async () => { + const filters = { + is_prn: prn, + prescription_type: "REGULAR", + }; + const res = await dispatch( - list({ is_prn: prn, prescription_type: "REGULAR" }) + list(showDiscontinued ? filters : { ...filters, discontinued: false }) ); setState({ @@ -74,7 +84,14 @@ export default function PrescriptionAdministrationsTable({ ), administrationsTimeBounds: getAdministrationBounds(res.data.results), }); - }, [consultation_id, dispatch]); + + if (showDiscontinued === false) { + const discontinuedRes = await dispatch( + list({ ...filters, discontinued: true, limit: 0 }) + ); + setDiscontinuedCount(discontinuedRes.data.count); + } + }, [consultation_id, showDiscontinued, dispatch]); useEffect(() => { refetch(); @@ -141,17 +158,22 @@ export default function PrescriptionAdministrationsTable({ } /> -
-
+
+
- - - )) - : pagination.slots?.map(({ start, end }, index) => ( - - ))} + : pagination.slots + ?.map(({ start, end }, index) => ( + + )) + .reverse()}
{t("medicine")} -

Dosage &

-

- {!state?.prescriptions[0]?.is_prn ? "Frequency" : "Indicator"} -

+
+
+ {t("medicine")} + +

Dosage &

+

+ {!state?.prescriptions[0]?.is_prn + ? "Frequency" + : "Indicator"} +

+
+
@@ -162,8 +184,10 @@ export default function PrescriptionAdministrationsTable({ border className="mx-2 px-1" variant="secondary" - disabled={!pagination.hasPrevious} - onClick={pagination.previous} + disabled={!pagination.hasNext} + onClick={pagination.next} + tooltip="Next 24 hours" + tooltipClassName="tooltip-bottom -translate-x-1/2 text-xs" > @@ -177,24 +201,26 @@ export default function PrescriptionAdministrationsTable({

-

{formatDateTime(start, "DD/MM")}

-

{formatDateTime(start, "HH:mm")}

- - - Administration(s) between -
- {formatTime(start)} and{" "} - {formatTime(end)} -
- on {formatDate(start)} -
-
+

{formatDateTime(end, "DD/MM")}

+

{formatDateTime(end, "HH:mm")}

+ + + Administration(s) between +
+ {formatTime(start)} and{" "} + {formatTime(end)} +
+ on {formatDate(start)} +
+
@@ -227,6 +255,23 @@ export default function PrescriptionAdministrationsTable({
+ {showDiscontinued === false && !!discontinuedCount && ( + setShowDiscontinued(true)} + > + + + + Show {discontinuedCount} other discontinued + prescription(s) + + + + )} + {state?.prescriptions.length === 0 && (
@@ -283,12 +328,7 @@ const PrescriptionRow = ({ prescription, ...props }: PrescriptionRowProps) => { }, [prescription.id, dispatch, props.intervals]); return ( - + <> {showDiscontinue && ( {
)} - setShowDetails(true)} + -
- - {prescription.medicine_object?.name ?? prescription.medicine_old} - + setShowDetails(true)} + > +
+
+ + {prescription.medicine_object?.name ?? + prescription.medicine_old} + - {prescription.discontinued && ( - - {t("discontinued")} - - )} + {prescription.discontinued && ( + + {t("discontinued")} + + )} - {prescription.route && ( - - {t(prescription.route)} - - )} -
- - - -

{prescription.dosage}

-

- {!prescription.is_prn - ? t("PRESCRIPTION_FREQUENCY_" + prescription.frequency) - : prescription.indicator} -

- - - - {/* Administration Cells */} - {props.intervals.map(({ start, end }, index) => ( - - {administrations === undefined ? ( - - ) : ( - - )} + {prescription.route && ( + + {t(prescription.route)} + + )} +
+ +
+

{prescription.dosage}

+

+ {!prescription.is_prn + ? t("PRESCRIPTION_FREQUENCY_" + prescription.frequency) + : prescription.indicator} +

+
+
- ))} - - - {/* Action Buttons */} - - setShowAdminister(true)} - > - {t("administer")} - - - + + + {/* Administration Cells */} + {props.intervals + .map(({ start, end }, index) => ( + + {administrations === undefined ? ( + + ) : ( + + )} + + )) + .reverse()} + + + {/* Action Buttons */} + + setShowAdminister(true)} + > + {t("administer")} + + + + ); }; diff --git a/src/Components/Patient/DailyRounds.tsx b/src/Components/Patient/DailyRounds.tsx index 351136e32d4..a84ad4a3e8b 100644 --- a/src/Components/Patient/DailyRounds.tsx +++ b/src/Components/Patient/DailyRounds.tsx @@ -42,7 +42,7 @@ const initForm: any = { other_symptoms: "", physical_examination_info: "", other_details: "", - patient_category: "Comfort", + patient_category: "", current_health: 0, recommend_discharge: false, action: null, @@ -142,14 +142,14 @@ export const DailyRounds = (props: any) => { ); if (!status.aborted) { - if (res && res.data) { + if (res?.data) { const data = { ...res.data, patient_category: res.data.patient_category ? PATIENT_CATEGORIES.find( (i) => i.text === res.data.patient_category - )?.id || "Comfort" - : "Comfort", + )?.id ?? "" + : "", rhythm: (res.data.rhythm && RHYTHM_CHOICES.find((i) => i.text === res.data.rhythm)?.id) || @@ -186,8 +186,8 @@ export const DailyRounds = (props: any) => { patient_category: res.data.patient_category ? PATIENT_CATEGORIES.find( (i) => i.text === res.data.patient_category - )?.id || "Comfort" - : "Comfort", + )?.id ?? "" + : "", rhythm: (res.data.rhythm && RHYTHM_CHOICES.find((i) => i.text === res.data.rhythm)?.id) || @@ -205,6 +205,12 @@ export const DailyRounds = (props: any) => { let invalidForm = false; Object.keys(state.form).forEach((field) => { switch (field) { + case "patient_category": + if (!state.form[field]) { + errors[field] = "Please select a category"; + invalidForm = true; + } + return; case "other_symptoms": if ( state.form.additional_symptoms?.includes(9) && diff --git a/src/Components/Patient/ManagePatients.tsx b/src/Components/Patient/ManagePatients.tsx index 54d22b5f54c..6e83eb9bc91 100644 --- a/src/Components/Patient/ManagePatients.tsx +++ b/src/Components/Patient/ManagePatients.tsx @@ -36,15 +36,14 @@ import RecordMeta from "../../CAREUI/display/RecordMeta"; import SearchInput from "../Form/SearchInput"; import SortDropdownMenu from "../Common/SortDropdown"; import SwitchTabs from "../Common/components/SwitchTabs"; -import SwipeableViews from "react-swipeable-views"; import { parseOptionId } from "../../Common/utils"; -import { parsePhoneNumberFromString } from "libphonenumber-js"; +import { formatAge, parsePhoneNumber } from "../../Utils/utils.js"; import { useDispatch } from "react-redux"; import useFilters from "../../Common/hooks/useFilters"; import { useTranslation } from "react-i18next"; import Page from "../Common/components/Page.js"; import dayjs from "dayjs"; -import { triggerGoal } from "../Common/Plausible.js"; +import { triggerGoal } from "../../Integrations/Plausible.js"; import useAuthUser from "../../Common/hooks/useAuthUser.js"; const Loading = lazy(() => import("../Common/Loading")); @@ -161,12 +160,10 @@ export const PatientManager = () => { (qParams.is_active || "True"), disease_status: qParams.disease_status || undefined, phone_number: qParams.phone_number - ? parsePhoneNumberFromString(qParams.phone_number)?.format("E.164") + ? parsePhoneNumber(qParams.phone_number) : undefined, emergency_phone_number: qParams.emergency_phone_number - ? parsePhoneNumberFromString(qParams.emergency_phone_number)?.format( - "E.164" - ) + ? parsePhoneNumber(qParams.emergency_phone_number) : undefined, local_body: qParams.lsgBody || undefined, facility: qParams.facility, @@ -561,7 +558,9 @@ export const PatientManager = () => {
{patient.name} - {`${patient.age} yrs.`} + + {formatAge(patient.age, patient.date_of_birth, true)} +
@@ -987,14 +986,12 @@ export const PatientManager = () => {
- - -
{managePatients}
-
- -
{managePatients}
-
-
+ +
{managePatients}
+
+ +
{managePatients}
+
import("../Common/Loading")); @@ -476,7 +476,12 @@ export const PatientHome = (props: any) => {

- {patientData.name} - {patientData.age} + {patientData.name} -{" "} + {formatAge( + patientData.age, + patientData.date_of_birth, + true + )}

{patientData.is_vaccinated ? ( diff --git a/src/Components/Patient/PatientInfoCard.tsx b/src/Components/Patient/PatientInfoCard.tsx index dbb4aac34ce..701b76b5e3a 100644 --- a/src/Components/Patient/PatientInfoCard.tsx +++ b/src/Components/Patient/PatientInfoCard.tsx @@ -20,7 +20,7 @@ import { PatientModel } from "./models"; import { getDimensionOrDash } from "../../Common/utils"; import useConfig from "../../Common/hooks/useConfig"; import { useState } from "react"; -import { formatDate, formatDateTime } from "../../Utils/utils.js"; +import { formatAge, formatDate, formatDateTime } from "../../Utils/utils.js"; import dayjs from "../../Utils/dayjs"; export default function PatientInfoCard(props: { @@ -171,7 +171,7 @@ export default function PatientInfoCard(props: {

)}

- {patient.age} years + {formatAge(patient.age, patient.date_of_birth, true)} โ€ข {patient.gender} {consultation?.suggestion === "DC" && ( diff --git a/src/Components/Patient/PatientNotes.tsx b/src/Components/Patient/PatientNotes.tsx index b7993ed1ea3..68a5ab4dc5d 100644 --- a/src/Components/Patient/PatientNotes.tsx +++ b/src/Components/Patient/PatientNotes.tsx @@ -101,10 +101,6 @@ const PatientNotes = (props: PatientNotesProps) => { }); }; - if (isLoading) { - return ; - } - return (

{

Add new notes