From 310e1a072e72da280733f47528e6b24d704c99c8 Mon Sep 17 00:00:00 2001 From: Arjun-Go Date: Mon, 20 May 2024 15:54:45 +0530 Subject: [PATCH 01/24] BAH-3701 | fix. Gruntfile to package next-ui css files --- ui/Gruntfile.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ui/Gruntfile.js b/ui/Gruntfile.js index 0509dec0a8..706bdf3d4b 100755 --- a/ui/Gruntfile.js +++ b/ui/Gruntfile.js @@ -22,7 +22,8 @@ module.exports = function (grunt) { 'components/ng-tags-input/ng-tags-input.min.css', 'components/jquery-ui/themes/smoothness/jquery-ui.min.css', 'micro-frontends-dist/shared.min.css', - 'micro-frontends-dist/ipd.min.css' + 'micro-frontends-dist/ipd.min.css', + 'micro-frontends-dist/next-ui.min.css' ]; var libraryJSFiles = [ From f5125e25544fd48ec9bfeaa53fd9c77b9655e89e Mon Sep 17 00:00:00 2001 From: Himabindu T Date: Tue, 21 May 2024 15:37:04 +0530 Subject: [PATCH 02/24] BAH-3803 | Add. Age Display Config For Patient Search In Registration * Umair | BAH-3803 |Adds new config for displaying patient search results * Kavitha | Add success popup After patient registration * Parvathy | BAH-3803 | Fix. Eslint Issues * [Rahul] | BAH-3803 | Refactor. Config Initialization --------- Co-authored-by: Umair Fayaz Co-authored-by: parvathy00 Co-authored-by: Rahul Ramesh --- ui/app/i18n/registration/locale_en.json | 5 +++-- .../controllers/searchPatientController.js | 12 ++++++++---- ui/app/registration/directives/patientAction.js | 4 ++++ ui/app/registration/views/search.html | 8 +++++--- 4 files changed, 20 insertions(+), 9 deletions(-) diff --git a/ui/app/i18n/registration/locale_en.json b/ui/app/i18n/registration/locale_en.json index 5a6fb682d5..837d205af4 100644 --- a/ui/app/i18n/registration/locale_en.json +++ b/ui/app/i18n/registration/locale_en.json @@ -122,5 +122,6 @@ "PATIENT_ATTRIBUTE_GIVEN_NAME_LOCAL": "First Name in Arabic", "PATIENT_ATTRIBUTE_FAMILY_NAME_LOCAL": "Last Name in Arabic", "PATIENT_ATTRIBUTE_MIDDLE_NAME_LOCAL": "Middle Name in Arabic", - "RELATED_PATIENT_IDENTIFIER_LABEL": "Patient" -} \ No newline at end of file + "RELATED_PATIENT_IDENTIFIER_LABEL": "Patient", + "REGISTRATION_LABEL_SAVE_REDIRECTION" : "Saved and Visit Started Successfully" +} diff --git a/ui/app/registration/controllers/searchPatientController.js b/ui/app/registration/controllers/searchPatientController.js index 1b99e719f9..9e88619a2b 100644 --- a/ui/app/registration/controllers/searchPatientController.js +++ b/ui/app/registration/controllers/searchPatientController.js @@ -5,12 +5,16 @@ angular.module('bahmni.registration') 'messagingService', '$translate', '$filter', function ($rootScope, $scope, $location, $window, spinner, patientService, appService, messagingService, $translate, $filter) { $scope.results = []; - $scope.extraIdentifierTypes = _.filter($rootScope.patientConfiguration.identifierTypes, function (identifierType) { - return !identifierType.primary; - }); var searching = false; var maxAttributesFromConfig = 5; - var allSearchConfigs = appService.getAppDescriptor().getConfigValue("patientSearch") || {}; + const allSearchConfigs = appService.getAppDescriptor().getConfigValue("patientSearch") || {}; + const patientSearchResultOptions = allSearchConfigs.patientSearchResultOptions != null ? allSearchConfigs.patientSearchResultOptions : {}; + const ignoredIdentifiers = new Set(patientSearchResultOptions.ignorePatientIdentifiers || []); + $scope.showAge = patientSearchResultOptions.showAge != null ? patientSearchResultOptions.showAge : true; + $scope.showDOB = patientSearchResultOptions.showDOB != null ? patientSearchResultOptions.showDOB : false; + $scope.extraIdentifierTypes = _.filter($rootScope.patientConfiguration.identifierTypes, function (identifierType) { + return !identifierType.primary && !ignoredIdentifiers.has(identifierType.name); + }); var patientSearchResultConfigs = appService.getAppDescriptor().getConfigValue("patientSearchResults") || {}; maxAttributesFromConfig = !_.isEmpty(allSearchConfigs.programAttributes) ? maxAttributesFromConfig - 1 : maxAttributesFromConfig; diff --git a/ui/app/registration/directives/patientAction.js b/ui/app/registration/directives/patientAction.js index e580f050d1..8878cad071 100644 --- a/ui/app/registration/directives/patientAction.js +++ b/ui/app/registration/directives/patientAction.js @@ -17,6 +17,7 @@ angular.module('bahmni.registration') defaultVisitType = defaultVisitType || appService.getAppDescriptor().getConfigValue('defaultVisitType'); var showStartVisitButton = appService.getAppDescriptor().getConfigValue("showStartVisitButton"); var forwardUrlsForVisitTypes = appService.getAppDescriptor().getConfigValue("forwardUrlsForVisitTypes"); + var showSuccessMessage = appService.getAppDescriptor().getConfigValue("showSuccessMessage"); showStartVisitButton = (_.isUndefined(showStartVisitButton) || _.isNull(showStartVisitButton)) ? true : showStartVisitButton; var visitLocationUuid = $rootScope.visitLocation; var forwardUrls = forwardUrlsForVisitTypes || false; @@ -163,6 +164,9 @@ angular.module('bahmni.registration') if (forwardUrl) { var updatedForwardUrl = appService.getAppDescriptor().formatUrl(forwardUrl, { 'patientUuid': patientProfileData.patient.uuid }); $window.location.href = updatedForwardUrl; + if (showSuccessMessage) { + messagingService.showMessage("info", "REGISTRATION_LABEL_SAVE_REDIRECTION"); + } } else { goToVisitPage(patientProfileData); } diff --git a/ui/app/registration/views/search.html b/ui/app/registration/views/search.html index 9e23de8a12..91ea7a714d 100644 --- a/ui/app/registration/views/search.html +++ b/ui/app/registration/views/search.html @@ -2,7 +2,7 @@
+ diff --git a/ui/app/common/displaycontrols/dashboard/views/dashboard.html b/ui/app/common/displaycontrols/dashboard/views/dashboard.html index 76242c47c9..a2d06ea45e 100644 --- a/ui/app/common/displaycontrols/dashboard/views/dashboard.html +++ b/ui/app/common/displaycontrols/dashboard/views/dashboard.html @@ -1,5 +1,5 @@ -
-
+
+
diff --git a/ui/app/styles/clinical/treatment/_treatment.scss b/ui/app/styles/clinical/treatment/_treatment.scss index b53e5d63ff..fd003fc5dc 100644 --- a/ui/app/styles/clinical/treatment/_treatment.scss +++ b/ui/app/styles/clinical/treatment/_treatment.scss @@ -380,7 +380,7 @@ } .order-set-container { - margin-top: 20px; + margin: 20px 0; } .order-section-container { diff --git a/ui/test/unit/clinical/common/services/allergyService.spec.js b/ui/test/unit/clinical/common/services/allergyService.spec.js new file mode 100644 index 0000000000..a4d5940542 --- /dev/null +++ b/ui/test/unit/clinical/common/services/allergyService.spec.js @@ -0,0 +1,786 @@ +describe('allergyService', function() { + var _$http, appService; + + beforeEach(module('bahmni.clinical')); + + beforeEach(module(function () { + _$http = jasmine.createSpyObj('$http', ['get', 'delete']); + })); + + beforeEach(module(function ($provide) { + _$http = jasmine.createSpyObj('$http', ['get']); + appService = jasmine.createSpyObj('appService', ['getAppDescriptor']); + var appDescriptor = jasmine.createSpyObj('appDescriptor', ['formatUrl']); + appDescriptor.formatUrl.and.returnValue({ + formatUrl: function(url, params) { + return url.replace('{patientUuid}', params.patientUuid); + } + }); + appService.getAppDescriptor.and.returnValue(appDescriptor); + $provide.value('$http', _$http); + $provide.value('appService',appService); + })); + + beforeEach(inject(['allergyService', function (allergyServiceInjected) { + allergyService = allergyServiceInjected; + }])); + + it('should get allergies correctly when call is successful', function() { + var patientUuid = '12345'; + var mockResponse = { + status: 200, + data: { + "resourceType": "Bundle", + "id": "f88a1cf9-d1d4-4c86-b77e-c50f08000768", + "meta": { + "lastUpdated": "2024-05-23T16:19:56.772+05:30", + "tag": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v3-ObservationValue", + "code": "SUBSETTED", + "display": "Resource encoded in summary mode" + } + ] + }, + "type": "searchset", + "total": 2, + "link": [ + { + "relation": "self", + "url": "http://localhost/openmrs/ws/fhir2/R4/AllergyIntolerance?_summary=data&patient=533ffc13-c06b-40b0-84db-ec2c8456e20b" + } + ], + "entry": [ + { + "fullUrl": "http://localhost/openmrs/ws/fhir2/R4/AllergyIntolerance/bc743662-6aa3-410d-916e-45236d292726", + "resource": { + "resourceType": "AllergyIntolerance", + "id": "bc743662-6aa3-410d-916e-45236d292726", + "meta": { + "lastUpdated": "2024-05-23T15:35:32.000+05:30", + "tag": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v3-ObservationValue", + "code": "SUBSETTED", + "display": "Resource encoded in summary mode" + } + ] + }, + "clinicalStatus": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/allergyintolerance-clinical", + "code": "active", + "display": "Active" + } + ], + "text": "Active" + }, + "verificationStatus": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/allergyintolerance-verification", + "code": "confirmed", + "display": "Confirmed" + } + ], + "text": "Confirmed" + }, + "type": "allergy", + "category": [ + "medication" + ], + "criticality": "high", + "code": { + "coding": [ + { + "code": "162298AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", + "display": "ACE inhibitors" + }, + { + "system": "http://snomed.info/sct", + "code": "41549009" + } + ] + }, + "patient": { + "reference": "Patient/533ffc13-c06b-40b0-84db-ec2c8456e20b", + "type": "Patient", + "display": "Rahul Ramesh (Patient Identifier: ABC200001)" + }, + "recordedDate": "2024-05-23T15:35:32+05:30", + "recorder": { + "reference": "Practitioner/bdea357f-f496-11ed-b179-0242ac150003", + "type": "Practitioner", + "display": "Super Man" + }, + "reaction": [ + { + "substance": { + "coding": [ + { + "code": "162298AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", + "display": "ACE inhibitors" + }, + { + "system": "http://snomed.info/sct", + "code": "41549009" + } + ] + }, + "manifestation": [ + { + "coding": [ + { + "code": "121677AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", + "display": "Mental status change" + }, + { + "system": "http://snomed.info/sct", + "code": "419284004" + } + ] + }, + { + "coding": [ + { + "code": "121629AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", + "display": "Anaemia" + }, + { + "system": "http://snomed.info/sct", + "code": "271737000" + } + ] + } + ], + "severity": "severe" + } + ] + } + }, + { + "fullUrl": "http://localhost/openmrs/ws/fhir2/R4/AllergyIntolerance/46aaf713-06e7-45a3-89b0-03d1ce8eeaa4", + "resource": { + "resourceType": "AllergyIntolerance", + "id": "46aaf713-06e7-45a3-89b0-03d1ce8eeaa4", + "meta": { + "lastUpdated": "2024-05-23T15:35:45.000+05:30", + "tag": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v3-ObservationValue", + "code": "SUBSETTED", + "display": "Resource encoded in summary mode" + } + ] + }, + "clinicalStatus": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/allergyintolerance-clinical", + "code": "active", + "display": "Active" + } + ], + "text": "Active" + }, + "verificationStatus": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/allergyintolerance-verification", + "code": "confirmed", + "display": "Confirmed" + } + ], + "text": "Confirmed" + }, + "type": "allergy", + "category": [ + "medication" + ], + "criticality": "low", + "code": { + "coding": [ + { + "code": "71617AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", + "display": "Aspirin" + }, + { + "system": "http://snomed.info/sct", + "code": "7947003" + } + ] + }, + "patient": { + "reference": "Patient/533ffc13-c06b-40b0-84db-ec2c8456e20b", + "type": "Patient", + "display": "Rahul Ramesh (Patient Identifier: ABC200001)" + }, + "recordedDate": "2024-05-23T15:35:45+05:30", + "recorder": { + "reference": "Practitioner/bdea357f-f496-11ed-b179-0242ac150003", + "type": "Practitioner", + "display": "Super Man" + }, + "reaction": [ + { + "substance": { + "coding": [ + { + "code": "71617AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", + "display": "Aspirin" + }, + { + "system": "http://snomed.info/sct", + "code": "7947003" + } + ] + }, + "manifestation": [ + { + "coding": [ + { + "code": "148888AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", + "display": "Anaphylaxis" + }, + { + "system": "http://snomed.info/sct", + "code": "39579001" + } + ] + } + ], + "severity": "mild" + } + ] + } + } + ] + } + }; + _$http.get.and.callFake(function () { + return mockResponse; + }); + var patientAllergyURL = appService.getAppDescriptor().formatUrl(Bahmni.Common.Constants.patientAllergiesURL, {'patientUuid': patientUuid}); + var result = allergyService.getAllergyForPatient(patientUuid); + + expect(_$http.get).toHaveBeenCalled(); + expect(_$http.get).toHaveBeenCalledWith(patientAllergyURL, { + method: "GET", + withCredentials: true, + cache: false + }); + expect(result).toBe(mockResponse); + }); + + it('should return an empty string if there are no allergies', function() { + var patientUuid = '12345'; + var mockResponse = { + status: 200, + data: { + "resourceType": "Bundle", + "id": "f88a1cf9-d1d4-4c86-b77e-c50f08000768", + "meta": { + "lastUpdated": "2024-05-23T16:19:56.772+05:30", + "tag": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v3-ObservationValue", + "code": "SUBSETTED", + "display": "Resource encoded in summary mode" + } + ] + }, + "type": "searchset", + "total": 2, + "link": [ + { + "relation": "self", + "url": "http://localhost/openmrs/ws/fhir2/R4/AllergyIntolerance?_summary=data&patient=533ffc13-c06b-40b0-84db-ec2c8456e20b" + } + ], + "entry": [ + { + "fullUrl": "http://localhost/openmrs/ws/fhir2/R4/AllergyIntolerance/bc743662-6aa3-410d-916e-45236d292726", + "resource": { + "resourceType": "AllergyIntolerance", + "id": "bc743662-6aa3-410d-916e-45236d292726", + "meta": { + "lastUpdated": "2024-05-23T15:35:32.000+05:30", + "tag": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v3-ObservationValue", + "code": "SUBSETTED", + "display": "Resource encoded in summary mode" + } + ] + }, + "clinicalStatus": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/allergyintolerance-clinical", + "code": "active", + "display": "Active" + } + ], + "text": "Active" + }, + "verificationStatus": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/allergyintolerance-verification", + "code": "confirmed", + "display": "Confirmed" + } + ], + "text": "Confirmed" + }, + "type": "allergy", + "category": [ + "medication" + ], + "criticality": "high", + "code": { + "coding": [ + { + "code": "162298AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", + "display": "ACE inhibitors" + }, + { + "system": "http://snomed.info/sct", + "code": "41549009" + } + ] + }, + "patient": { + "reference": "Patient/533ffc13-c06b-40b0-84db-ec2c8456e20b", + "type": "Patient", + "display": "Rahul Ramesh (Patient Identifier: ABC200001)" + }, + "recordedDate": "2024-05-23T15:35:32+05:30", + "recorder": { + "reference": "Practitioner/bdea357f-f496-11ed-b179-0242ac150003", + "type": "Practitioner", + "display": "Super Man" + }, + "reaction": [ + { + "substance": { + "coding": [ + { + "code": "162298AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", + "display": "ACE inhibitors" + }, + { + "system": "http://snomed.info/sct", + "code": "41549009" + } + ] + }, + "manifestation": [ + { + "coding": [ + { + "code": "121677AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", + "display": "Mental status change" + }, + { + "system": "http://snomed.info/sct", + "code": "419284004" + } + ] + }, + { + "coding": [ + { + "code": "121629AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", + "display": "Anaemia" + }, + { + "system": "http://snomed.info/sct", + "code": "271737000" + } + ] + } + ], + "severity": "severe" + } + ] + } + }, + { + "fullUrl": "http://localhost/openmrs/ws/fhir2/R4/AllergyIntolerance/46aaf713-06e7-45a3-89b0-03d1ce8eeaa4", + "resource": { + "resourceType": "AllergyIntolerance", + "id": "46aaf713-06e7-45a3-89b0-03d1ce8eeaa4", + "meta": { + "lastUpdated": "2024-05-23T15:35:45.000+05:30", + "tag": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v3-ObservationValue", + "code": "SUBSETTED", + "display": "Resource encoded in summary mode" + } + ] + }, + "clinicalStatus": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/allergyintolerance-clinical", + "code": "active", + "display": "Active" + } + ], + "text": "Active" + }, + "verificationStatus": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/allergyintolerance-verification", + "code": "confirmed", + "display": "Confirmed" + } + ], + "text": "Confirmed" + }, + "type": "allergy", + "category": [ + "medication" + ], + "criticality": "low", + "code": { + "coding": [ + { + "code": "71617AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", + "display": "Aspirin" + }, + { + "system": "http://snomed.info/sct", + "code": "7947003" + } + ] + }, + "patient": { + "reference": "Patient/533ffc13-c06b-40b0-84db-ec2c8456e20b", + "type": "Patient", + "display": "Rahul Ramesh (Patient Identifier: ABC200001)" + }, + "recordedDate": "2024-05-23T15:35:45+05:30", + "recorder": { + "reference": "Practitioner/bdea357f-f496-11ed-b179-0242ac150003", + "type": "Practitioner", + "display": "Super Man" + }, + "reaction": [ + { + "substance": { + "coding": [ + { + "code": "71617AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", + "display": "Aspirin" + }, + { + "system": "http://snomed.info/sct", + "code": "7947003" + } + ] + }, + "manifestation": [ + { + "coding": [ + { + "code": "148888AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", + "display": "Anaphylaxis" + }, + { + "system": "http://snomed.info/sct", + "code": "39579001" + } + ] + } + ], + "severity": "mild" + } + ] + } + } + ] + } + }; + _$http.get.and.callFake(function () { + return mockResponse; + }); + var result = allergyService.getAllergyForPatient(patientUuid); + + expect(result).toBe(mockResponse); + }); + + it('should handle errors gracefully', function() { + var patientUuid = '12345'; + var mockResponse = { + status: 500, + data: {} + }; + _$http.get.and.callFake(function () { + return mockResponse; + }); + var result = allergyService.fetchAndProcessAllergies(patientUuid); + + expect(result).toBe(""); + }); + + it('should handle errors gracefully', function() { + var patientUuid = '12345'; + var mockResponse = { + status: 500, + data: {} + }; + _$http.get.and.callFake(function () { + return mockResponse; + }); + + var result = allergyService.fetchAndProcessAllergies(patientUuid); + + expect(result).toBe(""); + }); + + it('should fetch and process allergies correctly', function() { + var patientUuid = '12345'; + var mockResponse = { + status: 200, + data: { + "resourceType": "Bundle", + "id": "f88a1cf9-d1d4-4c86-b77e-c50f08000768", + "meta": { + "lastUpdated": "2024-05-23T16:19:56.772+05:30", + "tag": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v3-ObservationValue", + "code": "SUBSETTED", + "display": "Resource encoded in summary mode" + } + ] + }, + "type": "searchset", + "total": 2, + "link": [ + { + "relation": "self", + "url": "http://localhost/openmrs/ws/fhir2/R4/AllergyIntolerance?_summary=data&patient=533ffc13-c06b-40b0-84db-ec2c8456e20b" + } + ], + "entry": [ + { + "fullUrl": "http://localhost/openmrs/ws/fhir2/R4/AllergyIntolerance/bc743662-6aa3-410d-916e-45236d292726", + "resource": { + "resourceType": "AllergyIntolerance", + "id": "bc743662-6aa3-410d-916e-45236d292726", + "meta": { + "lastUpdated": "2024-05-23T15:35:32.000+05:30", + "tag": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v3-ObservationValue", + "code": "SUBSETTED", + "display": "Resource encoded in summary mode" + } + ] + }, + "clinicalStatus": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/allergyintolerance-clinical", + "code": "active", + "display": "Active" + } + ], + "text": "Active" + }, + "verificationStatus": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/allergyintolerance-verification", + "code": "confirmed", + "display": "Confirmed" + } + ], + "text": "Confirmed" + }, + "type": "allergy", + "category": [ + "medication" + ], + "criticality": "high", + "code": { + "coding": [ + { + "code": "162298AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", + "display": "ACE inhibitors" + }, + { + "system": "http://snomed.info/sct", + "code": "41549009" + } + ] + }, + "patient": { + "reference": "Patient/533ffc13-c06b-40b0-84db-ec2c8456e20b", + "type": "Patient", + "display": "Rahul Ramesh (Patient Identifier: ABC200001)" + }, + "recordedDate": "2024-05-23T15:35:32+05:30", + "recorder": { + "reference": "Practitioner/bdea357f-f496-11ed-b179-0242ac150003", + "type": "Practitioner", + "display": "Super Man" + }, + "reaction": [ + { + "substance": { + "coding": [ + { + "code": "162298AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", + "display": "ACE inhibitors" + }, + { + "system": "http://snomed.info/sct", + "code": "41549009" + } + ] + }, + "manifestation": [ + { + "coding": [ + { + "code": "121677AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", + "display": "Mental status change" + }, + { + "system": "http://snomed.info/sct", + "code": "419284004" + } + ] + }, + { + "coding": [ + { + "code": "121629AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", + "display": "Anaemia" + }, + { + "system": "http://snomed.info/sct", + "code": "271737000" + } + ] + } + ], + "severity": "severe" + } + ] + } + }, + { + "fullUrl": "http://localhost/openmrs/ws/fhir2/R4/AllergyIntolerance/46aaf713-06e7-45a3-89b0-03d1ce8eeaa4", + "resource": { + "resourceType": "AllergyIntolerance", + "id": "46aaf713-06e7-45a3-89b0-03d1ce8eeaa4", + "meta": { + "lastUpdated": "2024-05-23T15:35:45.000+05:30", + "tag": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v3-ObservationValue", + "code": "SUBSETTED", + "display": "Resource encoded in summary mode" + } + ] + }, + "clinicalStatus": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/allergyintolerance-clinical", + "code": "active", + "display": "Active" + } + ], + "text": "Active" + }, + "verificationStatus": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/allergyintolerance-verification", + "code": "confirmed", + "display": "Confirmed" + } + ], + "text": "Confirmed" + }, + "type": "allergy", + "category": [ + "medication" + ], + "criticality": "low", + "code": { + "coding": [ + { + "code": "71617AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", + "display": "Aspirin" + }, + { + "system": "http://snomed.info/sct", + "code": "7947003" + } + ] + }, + "patient": { + "reference": "Patient/533ffc13-c06b-40b0-84db-ec2c8456e20b", + "type": "Patient", + "display": "Rahul Ramesh (Patient Identifier: ABC200001)" + }, + "recordedDate": "2024-05-23T15:35:45+05:30", + "recorder": { + "reference": "Practitioner/bdea357f-f496-11ed-b179-0242ac150003", + "type": "Practitioner", + "display": "Super Man" + }, + "reaction": [ + { + "substance": { + "coding": [ + { + "code": "71617AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", + "display": "Aspirin" + }, + { + "system": "http://snomed.info/sct", + "code": "7947003" + } + ] + }, + "manifestation": [ + { + "coding": [ + { + "code": "148888AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", + "display": "Anaphylaxis" + }, + { + "system": "http://snomed.info/sct", + "code": "39579001" + } + ] + } + ], + "severity": "mild" + } + ] + } + } + ] + } + }; + _$http.get.and.callFake(function () { + return mockResponse; + }); + var result = allergyService.fetchAndProcessAllergies(patientUuid); + + expect(result).toBe("ACE inhibitors, Aspirin"); + }); +}); diff --git a/ui/test/unit/clinical/consultation/controllers/treatmentController.spec.js b/ui/test/unit/clinical/consultation/controllers/treatmentController.spec.js new file mode 100644 index 0000000000..0a7531c5bf --- /dev/null +++ b/ui/test/unit/clinical/consultation/controllers/treatmentController.spec.js @@ -0,0 +1,46 @@ +'use strict'; + +describe('TreatmentController', function () { + var $controller, $rootScope, $scope, appService, mockAppDescriptor, filter; + var clinicalAppConfigService, cdssService; + beforeEach(module('bahmni.clinical')); + + beforeEach(inject(function (_$controller_, _$rootScope_, _$filter_) { + $controller = _$controller_; + $rootScope = _$rootScope_; + $scope = $rootScope.$new(); + clinicalAppConfigService = jasmine.createSpyObj('clinicalAppConfigService', ['getVisitTypeForRetrospectiveEntries']); + cdssService = jasmine.createSpyObj('cdssService', ['sortInteractionsByStatus']); + mockAppDescriptor = jasmine.createSpyObj('appDescriptor', ['getExtensionById']); + mockAppDescriptor.getExtensionById.and.returnValue({extensionParams: {sections: {allergies: {}}}}); + + appService = jasmine.createSpyObj('appService', ['getAppDescriptor']); + appService.getAppDescriptor.and.returnValue(mockAppDescriptor); + filter = _$filter_; + })); + + it('should initialize the newlyAddedTabTreatments', function () { + var controller = $controller('TreatmentController', { + $scope: $scope, + treatmentConfig: {drugOrderHistoryConfig: {view: 'default'}}, + $stateParams: {tabConfigName: 'allMedicationTabConfig'}, + appService: appService, + clinicalAppConfigService: clinicalAppConfigService, + cdssService: cdssService, + $filter: filter + }); + controller.initializeTreatments = jasmine.createSpy('initializeTreatments'); + $scope.consultation = { + newlyAddedTabTreatments: { + allMedicationTabConfig: { + treatments: [], + orderSetTreatments: [], + newOrderSet: "new order" + } + } + } + $scope.$apply(); + expect($scope.newOrderSet).toEqual("new order"); + }); + +}); From 6e9fe2f07648b9c36877def843f7bc0e050a1eb6 Mon Sep 17 00:00:00 2001 From: Soorya Kumaran C <90232857+SooryaKumaranC-tw@users.noreply.github.com> Date: Fri, 24 May 2024 18:42:44 +0530 Subject: [PATCH 06/24] BAH-3056 | Calculate rule based dosage (#640) * BAH-3056 | Calculate rule based dosage * Fix Merge conflict * Fix the dosage rule units based on units config map * Fix eslint issue * Parvathy | BAH-3056 | Add. Test Cases For Rule Based Dosage Calculation * Fix the dosage unit map ui issue * A-1206630318406475 | If Patient's weight is not captured, System should not allow Doctor's to save Rx (#864) * add. changes to add logic to add new treatment when weight is added * fix. uglify issues * fix. unit tests * add. test coverage fix * update. patient error msg * fix. test coverage * update. function call to set medication routes in config * add tests for coverage * BAH-3629 | Kavitha | optional fields for continuous medication (#859) * BAH-3629 | Kavitha | optional fields for continuous medication * Kavitha | fix eslint errors * Kavitha | refactored review comment * BAH-3056 | remove redundant code --------- Co-authored-by: parvathy00 Co-authored-by: Arjun G <91885483+Arjun-Go@users.noreply.github.com> Co-authored-by: Arjun-Go Co-authored-by: Kavitha S Co-authored-by: kavitha-sundararajan <90255023+kavitha-sundararajan@users.noreply.github.com> --- .../controllers/addTreatmentController.js | 104 +++++++++++- .../consultation/views/newDrugOrders.html | 12 +- .../views/treatmentSections/addTreatment.html | 29 +++- .../treatmentSections/drugOrderHistory.html | 12 +- ui/app/i18n/clinical/locale_en.json | 4 + .../addTreatmentController.spec.js | 154 +++++++++++++++++- 6 files changed, 291 insertions(+), 24 deletions(-) diff --git a/ui/app/clinical/consultation/controllers/addTreatmentController.js b/ui/app/clinical/consultation/controllers/addTreatmentController.js index 2829579077..e81197cb90 100644 --- a/ui/app/clinical/consultation/controllers/addTreatmentController.js +++ b/ui/app/clinical/consultation/controllers/addTreatmentController.js @@ -3,10 +3,10 @@ angular.module('bahmni.clinical') .controller('AddTreatmentController', ['$scope', '$rootScope', 'contextChangeHandler', 'treatmentConfig', 'drugService', '$timeout', 'clinicalAppConfigService', 'ngDialog', '$window', 'messagingService', 'appService', 'activeDrugOrders', - 'orderSetService', '$q', 'locationService', 'spinner', '$translate', '$state', 'cdssService', + 'orderSetService', '$q', 'locationService', 'spinner', '$translate', '$state', 'observationsService', 'cdssService', function ($scope, $rootScope, contextChangeHandler, treatmentConfig, drugService, $timeout, - clinicalAppConfigService, ngDialog, $window, messagingService, appService, activeDrugOrders, - orderSetService, $q, locationService, spinner, $translate, $state, cdssService) { + clinicalAppConfigService, ngDialog, $window, messagingService, appService, activeDrugOrders, + orderSetService, $q, locationService, spinner, $translate, $state, observationsService, cdssService) { var DateUtil = Bahmni.Common.Util.DateUtil; var DrugOrderViewModel = Bahmni.Clinical.DrugOrderViewModel; var scrollTop = _.partial($window.scrollTo, 0, 0); @@ -62,6 +62,13 @@ angular.module('bahmni.clinical') return !!($scope.doseFractions && !_.isEmpty($scope.doseFractions)); }; + $scope.isRuleMode = function (treatment) { + if (treatment.dosingRule != undefined || treatment.dosingRule != null) { + return true; + } + return false; + }; + $scope.isSelected = function (drug) { var selectedDrug = $scope.treatment.drug; return selectedDrug && drug.drug.name === selectedDrug.name; @@ -355,8 +362,39 @@ angular.module('bahmni.clinical') $scope.treatment.calculateQuantityAndUnit(); }, true); + $scope.calculateDose = function (treatment) { + if (treatment.dosingRule != null || treatment.dosingRule != undefined) { + var visitUuid = treatmentConfig.orderSet.calculateDoseOnlyOnCurrentVisitValues ? $scope.activeVisit.uuid : undefined; + var calculatedDose = orderSetService.getCalculatedDose($scope.patient.uuid, treatment.drug.name, treatment.uniformDosingType.dose, treatment.uniformDosingType.doseUnits, '', treatment.dosingRule, visitUuid); + calculatedDose.then(function (calculatedDosage) { + treatment.uniformDosingType.dose = calculatedDosage.dose; + treatment.calculateQuantityAndUnit(); + return treatment; + }); + return treatment; + } + }; + + $scope.$watch('calculateDose', $scope.treatment, true); + + $scope.getFinalDosingUnits = function (treatment) { + const ruleUnitList = $scope.ruleUnitsMap && $scope.ruleUnitsMap[treatment.dosingRule] ? $scope.ruleUnitsMap[treatment.dosingRule] : []; + if ($scope.isRuleMode(treatment) && ruleUnitList.length > 0) { + return treatmentConfig.getDoseUnits().filter(function (unitMap) { + if (ruleUnitList.includes(unitMap.name)) { + return unitMap; + } + }); + } else { + return treatmentConfig.getDoseUnits(); + } + }; + $scope.add = function () { var treatments = $scope.treatments; + if ($scope.addTreatmentWithPatientWeight.hasOwnProperty('duration') && ($scope.obs.length == 0 || (($scope.currentEpoch - $scope.obs[0].observationDateTime) / 1000 > $scope.addTreatmentWithPatientWeight.duration))) { + return; + } if ($scope.treatment.isNewOrderSet) { treatments = $scope.orderSetTreatments; } @@ -824,6 +862,55 @@ angular.module('bahmni.clinical') }); }; + var showRulesInMedication = function (medicationConfig) { + $scope.showRulesInMedication = false; + if (medicationConfig !== 'undefined' && medicationConfig.tabConfig !== 'undefined' && medicationConfig.tabConfig.allMedicationTabConfig + !== 'undefined' && medicationConfig.tabConfig.allMedicationTabConfig.orderSet !== 'undefined') { + if (medicationConfig.tabConfig.allMedicationTabConfig.orderSet.showRulesInMedication) { + $scope.showRulesInMedication = true; + $scope.ruleUnitsMap = medicationConfig.tabConfig.allMedicationTabConfig.orderSet.dosageRuleUnitsMap || []; + } + } + }; + + $scope.$watch('treatment.route', function (newValue, oldValue) { + if (newValue !== oldValue) { + $scope.checkForContinuousMedication(newValue); + } + }); + + $scope.$watch('treatment.quantity', function (newValue) { + if (newValue === 0 && $scope.isContinuousMedication && !$scope.treatment.uniformDosingType.frequency) { + $scope.treatment.quantity = null; + } + }); + + $scope.checkForContinuousMedication = function (route) { + $scope.isContinuousMedication = $scope.continuousMedicationRoutes.includes(route); + }; + + var setContinuousMedicationRoutes = function (medicationConfig) { + $scope.continuousMedicationRoutes = []; + if (medicationConfig && medicationConfig.tabConfig && medicationConfig.tabConfig.allMedicationTabConfig + && medicationConfig.tabConfig.allMedicationTabConfig.inputOptionsConfig) { + $scope.continuousMedicationRoutes = medicationConfig.tabConfig.allMedicationTabConfig.inputOptionsConfig.continuousMedicationRoutes || []; + } + }; + + $scope.verifyAdd = function (treatment) { + if (!$scope.addTreatmentWithPatientWeight.hasOwnProperty('duration')) { + return $scope.addForm.$valid && $scope.calculateDose(treatment); + } else { + if ($scope.obs.length > 0 && (($scope.currentEpoch - $scope.obs[0].observationDateTime) / 1000 <= $scope.addTreatmentWithPatientWeight.duration)) { + $scope.addToNewTreatment = true; + return $scope.addForm.$valid && $scope.calculateDose(treatment); + } else { + $scope.addToNewTreatment = false; + messagingService.showMessage("error", $translate.instant("ENTER_PATIENT_WEIGHT_ERROR")); + } + } + }; + var init = function () { $scope.consultation.removableDrugs = $scope.consultation.removableDrugs || []; $scope.consultation.discontinuedDrugs = $scope.consultation.discontinuedDrugs || []; @@ -833,6 +920,17 @@ angular.module('bahmni.clinical') mergeActiveAndScheduledWithDiscontinuedOrders(); $scope.treatmentConfig = treatmentConfig;// $scope.treatmentConfig used only in UI + var medicationConfig = appService.getAppDescriptor().getConfigForPage('medication') || {}; + $scope.addTreatmentWithPatientWeight = appService.getAppDescriptor().getConfigValue('addTreatmentWithPatientWeight') || {}; + if ($scope.addTreatmentWithPatientWeight.hasOwnProperty('duration')) { + observationsService.fetch($scope.patient.uuid, $scope.addTreatmentWithPatientWeight.conceptNames, "latest", 1, null, null, null, null).then(function (response) { + $scope.currentEpoch = Math.floor(new Date().getTime() / 1000) * 1000; + $scope.obs = response.data; + }); + } + $scope.addToNewTreatment = true; + showRulesInMedication(medicationConfig); + setContinuousMedicationRoutes(medicationConfig); }; init(); }]); diff --git a/ui/app/clinical/consultation/views/newDrugOrders.html b/ui/app/clinical/consultation/views/newDrugOrders.html index dd991714cb..40605680f6 100644 --- a/ui/app/clinical/consultation/views/newDrugOrders.html +++ b/ui/app/clinical/consultation/views/newDrugOrders.html @@ -40,9 +40,9 @@

{{ ::'MEDICATION_NEW_PRESCRIPTION' | translate}}

- {{newTreatment.getDoseAndUnits()}} | - {{newTreatment.getFrequency()}} | - {{newTreatment.getDurationAndDurationUnits()}} + {{newTreatment.getDoseAndUnits()}} + | {{newTreatment.getFrequency()}} + | {{newTreatment.getDurationAndDurationUnits()}}
@@ -67,12 +67,12 @@

{{ ::'MEDICATION_NEW_PRESCRIPTION' | translate}} {{newTreatment.getAsNeededText(newTreatment.asNeeded)}}, {{newTreatment.route}}, - {{newTreatment.instructions}} | + {{newTreatment.instructions}}

- {{newTreatment.additionalInstructions}} | + | {{newTreatment.additionalInstructions}}
-
{{newTreatment.getQuantityWithUnit()}}
+
| {{newTreatment.getQuantityWithUnit()}}
diff --git a/ui/app/clinical/consultation/views/treatmentSections/addTreatment.html b/ui/app/clinical/consultation/views/treatmentSections/addTreatment.html index a8f5acad50..5140e59b72 100644 --- a/ui/app/clinical/consultation/views/treatmentSections/addTreatment.html +++ b/ui/app/clinical/consultation/views/treatmentSections/addTreatment.html @@ -42,8 +42,20 @@

+
+
+ +
+
+ +
+
@@ -81,7 +93,7 @@

@@ -109,7 +121,7 @@

@@ -178,7 +190,7 @@

+ ng-required="treatment.isDurationRequired && !isContinuousMedication" ng-pattern="/^[0-9]+$/"/>
@@ -189,7 +201,7 @@

+ ng-required="!isContinuousMedication">

@@ -203,7 +215,7 @@

+ ng-required="treatment.isDurationRequired && !isContinuousMedication"/>
@@ -262,7 +274,8 @@

diff --git a/ui/app/clinical/consultation/views/treatmentSections/drugOrderHistory.html b/ui/app/clinical/consultation/views/treatmentSections/drugOrderHistory.html index b74ba98407..f59a661eb8 100755 --- a/ui/app/clinical/consultation/views/treatmentSections/drugOrderHistory.html +++ b/ui/app/clinical/consultation/views/treatmentSections/drugOrderHistory.html @@ -34,9 +34,9 @@
- {{drugOrder.getDoseAndUnits()}} | - {{drugOrder.getFrequency()}} | - {{drugOrder.getDurationAndDurationUnits()}} + {{drugOrder.getDoseAndUnits()}} + | {{drugOrder.getFrequency()}} + | {{drugOrder.getDurationAndDurationUnits()}}
@@ -78,10 +78,10 @@
{{drugOrder.getAsNeededText(drugOrder.asNeeded)}}, {{drugOrder.route}}, - {{drugOrder.instructions}} | + {{drugOrder.instructions}}
-
{{drugOrder.additionalInstructions}} |
-
{{drugOrder.getQuantityWithUnit()}}
+
| {{drugOrder.additionalInstructions}}
+
| {{drugOrder.getQuantityWithUnit()}}

diff --git a/ui/app/clinical/consultation/views/treatmentSections/drugOrderHistory.html b/ui/app/clinical/consultation/views/treatmentSections/drugOrderHistory.html index f59a661eb8..8880bec570 100755 --- a/ui/app/clinical/consultation/views/treatmentSections/drugOrderHistory.html +++ b/ui/app/clinical/consultation/views/treatmentSections/drugOrderHistory.html @@ -52,7 +52,7 @@ {{orderAttribute.name.substring(0,1)}} - - - - - - +
@@ -116,6 +120,7 @@
+ diff --git a/ui/app/clinical/index.html b/ui/app/clinical/index.html index 7342167fbe..aaef2526ab 100644 --- a/ui/app/clinical/index.html +++ b/ui/app/clinical/index.html @@ -558,8 +558,8 @@

+ - diff --git a/ui/app/common/domain/services/diagnosisService.js b/ui/app/common/domain/services/diagnosisService.js index 76788f907d..459990dac4 100644 --- a/ui/app/common/domain/services/diagnosisService.js +++ b/ui/app/common/domain/services/diagnosisService.js @@ -67,4 +67,11 @@ angular.module('bahmni.common.domain') return consultation; }); }; + + this.getPatientDiagnosis = function (patientUuid) { + var url = Bahmni.Common.Constants.bahmniDiagnosisUrl; + return $http.get(url, { + params: { patientUuid: patientUuid } + }); + }; }]); diff --git a/ui/app/common/domain/services/providerService.js b/ui/app/common/domain/services/providerService.js index d1fd1875a1..526a48487e 100644 --- a/ui/app/common/domain/services/providerService.js +++ b/ui/app/common/domain/services/providerService.js @@ -1,7 +1,7 @@ 'use strict'; angular.module('bahmni.common.domain') - .factory('providerService', ['$http', function ($http) { + .factory('providerService', ['$http', 'appService', function ($http, appService) { var search = function (fieldValue) { return $http.get(Bahmni.Common.Constants.providerUrl, { method: "GET", @@ -28,9 +28,19 @@ angular.module('bahmni.common.domain') }); }; + var getAttributesForProvider = function (providerUuid) { + var providerAttributeUrl = appService.getAppDescriptor().formatUrl(Bahmni.Common.Constants.providerAttributeUrl, {'providerUuid': providerUuid}); + return $http.get(providerAttributeUrl, { + method: "GET", + withCredentials: true, + cache: false + }); + }; + return { search: search, searchByUuid: searchByUuid, - list: list + list: list, + getAttributesForProvider: getAttributesForProvider }; }]); diff --git a/ui/app/common/patient/mappers/patientMapper.js b/ui/app/common/patient/mappers/patientMapper.js index d591a46ae9..5bfe11a0ee 100644 --- a/ui/app/common/patient/mappers/patientMapper.js +++ b/ui/app/common/patient/mappers/patientMapper.js @@ -85,10 +85,13 @@ Bahmni.PatientMapper = function (patientConfig, $rootScope, $translate) { "address1": preferredAddress.address1, "address2": preferredAddress.address2, "address3": preferredAddress.address3, + "address4": preferredAddress.address4, + "address5": preferredAddress.address5, "cityVillage": preferredAddress.cityVillage, "countyDistrict": preferredAddress.countyDistrict === null ? '' : preferredAddress.countyDistrict, "stateProvince": preferredAddress.stateProvince, - "postalCode": preferredAddress.postalCode ? preferredAddress.postalCode : "" + "postalCode": preferredAddress.postalCode ? preferredAddress.postalCode : "", + "country": preferredAddress.country ? preferredAddress.country : "" } : {}; }; diff --git a/ui/app/clinical/common/services/allergyService.js b/ui/app/common/services/allergyService.js similarity index 97% rename from ui/app/clinical/common/services/allergyService.js rename to ui/app/common/services/allergyService.js index cd3fd9f32c..dba6b7393c 100644 --- a/ui/app/clinical/common/services/allergyService.js +++ b/ui/app/common/services/allergyService.js @@ -1,6 +1,6 @@ 'use strict'; -angular.module('bahmni.clinical') +angular.module('bahmni.common.util') .factory('allergyService', ['$http', 'appService', function ($http, appService) { var getAllergyForPatient = function (patientUuid) { var patientAllergyURL = appService.getAppDescriptor().formatUrl(Bahmni.Common.Constants.patientAllergiesURL, {'patientUuid': patientUuid}); diff --git a/ui/app/common/ui-helper/printer.js b/ui/app/common/ui-helper/printer.js index 7cf3ebc737..d0cde298d8 100644 --- a/ui/app/common/ui-helper/printer.js +++ b/ui/app/common/ui-helper/printer.js @@ -30,7 +30,8 @@ angular.module('bahmni.common.uiHelper') }, false); }; - var print = function (templateUrl, data) { + var print = function (templateUrl, data, pageTitle) { + pageTitle = pageTitle || null; $rootScope.isBeingPrinted = true; $http.get(templateUrl).then(function (templateData) { var template = templateData.data; @@ -38,6 +39,8 @@ angular.module('bahmni.common.uiHelper') angular.extend(printScope, data); var element = $compile($('
' + template + '
'))(printScope); var renderAndPrintPromise = $q.defer(); + var originalTitle = angular.element(document).prop('title'); + pageTitle ? angular.element(document).prop('title', pageTitle) : angular.element(document).prop('title', originalTitle); var waitForRenderAndPrint = function () { if (printScope.$$phase || $http.pendingRequests.length) { $timeout(waitForRenderAndPrint, 1000); @@ -46,6 +49,7 @@ angular.module('bahmni.common.uiHelper') printHtml(element.html()).then(function () { $rootScope.isBeingPrinted = false; renderAndPrintPromise.resolve(); + angular.element(document).prop('title', originalTitle); }); printScope.$destroy(); } diff --git a/ui/app/i18n/clinical/locale_en.json b/ui/app/i18n/clinical/locale_en.json index 22ddfff148..3ccd5f208a 100644 --- a/ui/app/i18n/clinical/locale_en.json +++ b/ui/app/i18n/clinical/locale_en.json @@ -1,389 +1,392 @@ { - "CONSULTATION_TAB_KEY": "Consultation", - "CONSULTATION_SHORTCUT_KEY": "c", - "NO_ACTIVE_PASSIVE_PROGRAMS_MESSAGE": "No active/inactive programs for this patient.", - "NO_TREATMENTS_MESSAGE": "No treatments for this patient.", - "NO_ADMISSION_DETAILS_MESSAGE": "No Admission Details for this patient.", - "NO_DOCUMENTS_MESSAGE": "No documents for this patient.", - "NO_DISPOSTIONS_AVAILABLE_MESSAGE_KEY": "No dispositions available.", - "NO_LAB_ORDERS_FOR_PATIENT_MESSAGE_KEY": "No Lab Orders for this patient.", - "DISPLAY_CONTROL_ERROR_MESSAGE_KEY": "No {{ displayControl }} recorded for this patient", - "NO_DIAGNOSIS_MESSAGE": "No diagnosis for this patient", - "NO_SPECIMEN_RESULT_FOUND_MESSAGE": "No Result found for this specimen", - "NO_SPECIMEN_FOUND_MESSAGE": "No specimen for this patient", - "NO_DATA_MESSAGE": "No data captured.", - "NO_DRUG_DETAILS_MESSAGE": "No Drugs to show for this patient", - "DOB_LABEL": "Date of Birth", - "ALL_LAB_RESULTS_LABEL": "All Lab Results", - "ACCESSION_AT_LABEL": "Accession at", - "SAMPLE_AT_LABEL": "Sample at", - "PROGRAM_MANAGEMENT_SELECT_PROGRAM_KEY": "New Program Enrollment", - "PROGRAM_MANAGEMENT_ACTIVE_PROGRAM_KEY": "Active Programs", - "PROGRAM_MANAGEMENT_INACTIVE_PROGRAM_KEY": "InActive Programs", - "PROGRAM_MANAGEMENT_ACTIVE_TREATMENT_KEY": "Active Treatments", - "PROGRAM_MANAGEMENT_INACTIVE_TREATMENT_KEY": "InActive Treatments", - "PROGRAM_MANAGEMENT_PAST_PROGRAM_KEY": "Past Programs", - "PROGRAM_MANAGEMENT_PROGRAM_STATES_KEY": "Program States", - "PROGRAM_MANAGEMENT_PROGRAM_ATTRIBUTES_KEY": "Program Attributes", - "PROGRAM_MANAGEMENT_LABEL_PROGRAM": "Program", - "PROGRAM_MANAGEMENT_CHOOSE_PROGRAM_KEY": "Choose Program", - "PROGRAM_MANAGEMENT_LABEL_PROGRAM_STATE": "Program State", - "PROGRAM_MANAGEMENT_LABEL_PROGRAM_OUTCOME": "Program Outcome", - "PROGRAM_MANAGEMENT_LABEL_CHOOSE_OUTCOME": "Choose Outcome", - "PROGRAM_MANAGEMENT_CHOOSE_STATE_KEY": "Choose Program State", - "PROGRAM_MANAGEMENT_CHANGE_STATE_KEY": "Change Program State", - "PROGRAM_MANAGEMENT_START_DATE_KEY": "Start Date", - "PROGRAM_MANAGEMENT_STOP_DATE_KEY": "Stop Date", - "PROGRAM_MANAGEMENT_STARTED_ON_KEY": "Started on", - "PROGRAM_MANAGEMENT_SELECT_PROGRAM_MESSAGE_KEY": "Please select a Program to Enroll the patient", - "PROGRAM_MANAGEMENT_ALREADY_ENROLLED_PROGRAM_MESSAGE_KEY": "Patient already enrolled to the Program", - "PROGRAM_MANAGEMENT_STATE_CANT_START_BEFORE_KEY": "State cannot be started earlier than current state", - "PROGRAM_MANAGEMENT_PROGRAM_CANT_END_BEFORE_KEY": "Program cannot be ended earlier than current state", - "PROGRAM_MANAGEMENT_DO_YOU_WANT_TO_REMOVE_KEY": "Do you want to remove", - "PATIENT_NOT_ENROLLED_TO_PROGRAMS_MESSAGE_KEY": "The Patient has not enrolled to any programs", - "PATIENT_DOESNT_HAVE_PREVIOUS_PROGRAMS_MESSAGE_KEY": "The Patient doesn't have any previous programs", - "PROGRAM_MANAGEMENT_ENROLL_KEY": "Enroll", - "PROGRAM_MANAGEMENT_TO_KEY": "to", - "PROGRAM_MANAGEMENT_ON_KEY": "on", - "PROGRAM_MANAGEMENT_BY_KEY": "By", - "PROGRAM_MANAGEMENT_End_PROGRAM_KEY": "End Program", - "PROGRAM_MANAGEMENT_DETAILS_KEY": "Details", - "PROGRAM_MANAGEMENT_GENERAL_CONSULTATION_KEY": "General Consultation", - "PROGRAM_DISPLAY_CONTROL_TREATMENT_STATES_KEY": "Treatment States", - "PROGRAM_DISPLAY_CONTROL_TREATMENT_START_DATE_KEY": "Treatment Start Date", - "PROGRAM_DISPLAY_CONTROL_TREATMENT_STOP_DATE_KEY": "Treatment Stop Date", - "PROGRAM_DISPLAY_CONTROL_FACILITY_OF_ENROLLMENT_KEY": "Facility of Enrollment", - "TREATMENTS_SUMMARY_PRESCRIPTION_KEY": "Prescriptions", - "INVESTIGATION_CHART_KEY": "Investigation Chart", - "LAB_ORDERS_KEY": "Lab Orders", - "ALL_VISITS_KEY": "All Visits", - "ALL_DIAGNOSES_KEY": "All Diagnoses", - "ALL_KEY": "All", - "SEARCH_NAME_PLACEHOLDER_KEY": "Search Name/{{ primaryIdentifier }} ...", - "SEARCH_KEY": "Search", - "CLINICAL_YEARS_TRANSLATION_KEY": "years", - "CLINICAL_MONTHS_TRANSLATION_KEY": "months", - "CLINICAL_DAYS_TRANSLATION_KEY": "days", - "CONCEPT_SET_GROUP_EXPAND_ALL_KEY": "Expand All", - "CONCEPT_SET_GROUP_COLLAPSE_ALL_KEY": "Collapse All", - "ADD_NEW_OBSERVATION": "Add New Obs Form", - "CONCEPT_SET_GROUP_PIN_UNPIN_KEY": "Pin/Unpin", - "CONCEPT_SET_GROUP_REMOVE_KEY": "Remove", - "CONCEPT_SET_GROUP_SEARCH_LABEL": "Search Obs Form", - "EMPTY_TEMPLATE_MESSAGE": "Click on 'Add New Obs Form' button to add new forms here", - "SCROLL_LEFT": "Scroll to Left", - "SCROLL_RIGHT": "Scroll to Right", - "VISIT_TITLE_PATIENT_INFORMATION": "PatientInformation", - "VISIT_TITLE_DIAGNOSIS_KEY": "Diagnoses", - "VISIT_TITLE_DISPOSITIONS_KEY": "Dispositions", - "VISIT_TITLE_DISPOSITION_KEY": "Disposition", - "VISIT_TITLE_ADMISSION_DETAILS_KEY": "Admission Details", - "VISIT_TITLE_LAB_INVESTIGATIONS_KEY": "Lab Investigations", - "VISIT_TITLE_TREATMENTS_KEY": "Treatments", - "VISIT_TITLE_PATIENT_FILES_KEY": "Patient Files", - "VISIT_TITLE_RADIOLOGY_DOCUMENTS_KEY": "Radiology Documents", - "VISIT_TITLE_OBSERVATIONS_KEY": "Observations", - "ADD_MORE_KEY": "Add More", - "ADD_SECTION_KEY": "Add Section", - "ADD_FORM_KEY": "Add Form", - "CLINICAL_SAVE_TRANSLATION_KEY": "Save", - "CLINICAL_SAVE_SUCCESS_MESSAGE_KEY": "Saved", - "CLINICAL_SAVE_FAILURE_MESSAGE_KEY": "An error has occurred on the server. Information not saved.", - "CLINICAL_FORM_ERRORS_MESSAGE_KEY": "Please enter a value in the mandatory fields or correct the value in the highlighted fields to proceed", - "CLINICAL_FORM_EDIT_ERROR_MESSAGE_KEY": "Form not found", - "CLINICAL_ORDER_RADIOLOGY_NEED_PRINT": "Need Print for this order.", - "CLINICAL_ORDER_RADIOLOGY_NEED_PRINT_BUTTON": "Needs Print", - "CLINICAL_DECIMAL_FAILURE_MESSAGE_KEY": "Please enter Integer value, decimal value is not allowed", - "SAVE_ACCESS_KEY": "s", - "CLINICAL_PRINT_TRANSLATION_KEY": "Print", - "CLINICAL_PRINT_ACCESS_KEY": "r", - "CLINICAL_SHARE_PRESCRIPTION_KEY": "Share or print the prescription", - "VISIT_ON_TRANSLATION_KEY": "Visit On", - "ADD_TO_DRUG_CHART_KEY": "Add to Drug Chart", - "IPD_BUTTON": "IPD", - "ENTERING_DATA_FOR_TRANSLATION_KEY": "Entering data for", - "EDITITNG_DATA_FOR_ENCOUNTER_TRANSLATION_KEY": "Editing data for encounter", - "ON_BEHALF_OF_TRANSLATION_KEY": "On behalf of", - "PATIENT_DOCUMENT_TRANSLATION_KEY": "Patient Documents", - "BAHMNI_HOME_TRANSLATION_KEY": "Bahmni Home", - "PATIENT_QUEUE_TRANSLATION_KEY": "Patient Queue", - "LOGOUT_TRANSLATION_KEY": "Logout", - "OBS_ABNORMAL_TRANSLATE_KEY": "Abnormal", - "OBS_UNKNOWN_TRANSLATE_KEY": "Unknown", - "LAB_INVESTIGATIONS_KEY": "Lab Investigations", - "BACTERIOLOGY_SAMPLE_COLLECTION_DATE_KEY": "Date of Sample Collection", - "BACTERIOLOGY_SAMPLE_TYPE_KEY": "Type of sample", - "BACTERIOLOGY_SAMPLE_ID_KEY": "Sample Id", - "BACTERIOLOGY_TAB_TITLE_KEY": "Sample Details", - "BACTERIOLOGY_BUTTON_ADD_SAMPLE_KEY": "ADD SAMPLE", - "BACTERIOLOGY_BUTTON_CLEAR_KEY": "Clear", - "BACTERIOLOGY_BUTTON_EDIT_KEY": "Edit", - "BACTERIOLOGY_ADDED_ON_KEY": "Added on", - "BACTERIOLOGY_SAMPLE_TYPE_KEY_NON_CODED": "Other Sample Type", - "MEDICATION_RECENT_TAB": "Recent", - "MEDICATION_RECENT_TAB_STOP_REASON": "Stop Reason", - "MEDICATION_RECENT_TAB_STOP_NOTES": "Notes", - "MEDICATION_TAB_DOSE": "Dose", - "MEDICATION_DEFAULT_DRUGS_TITLE": "Frequently Used", - "MEDICATION_DRUG_NAME_TITLE": "Drug Name", - "MEDICATION_ADD_DRUG_FORM_TITLE": "Order Drug", - "MEDICATION_ADD_ORDERSET_FORM_TITLE": "Order an Order Set", - "MEDICATION_LABEL_DOSE": "Dose", - "MEDICATION_LABEL_UNITS": "Units", - "MEDICATION_LABEL_FREQUENCY": "Frequency", - "MEDICATION_LABEL_ROUTE": "Route", - "MEDICATION_LABEL_DURATION": "Duration", - "MEDICATION_LABEL_TOTAL_QUANTITY": "Total Quantity", - "MEDICATION_LABEL_AS_NEEDED": "As Needed", - "MEDICATION_DOSE_UNIT_PLACEHOLDER": "Choose Unit", - "MEDICATION_FREQUENCY_UNIT_PLACEHOLDER": "Choose Frequency", - "MEDICATION_ROUTE_PLACEHOLDER": "Choose Route", - "MEDICATION_CHOOSE_UNIT_PLACEHOLDER": "Choose Unit", - "MEDICATION_CHOOSE_QUANTITY_UNIT_PLACEHOLDER": "Choose Unit", - "MEDICATION_AS_NEEDED": "SOS", - "MEDICATION_AS_NEEDED_BUTTON_PLACEHOLDER": "SOS", - "MEDICATION_AS_NEEDED_ACCESS_KEY": "o", - "MEDICATION_LABEL_DOSING_INSTRUCTIONS": "Instructions", - "MEDICATION_DOSING_INSTRUCTIONS_PLACEHOLDER": "Choose Instruction", - "MEDICATION_LABEL_ADDITIONAL_INSTRUCTIONS": "Additional Instructions", - "MEDICATION_ADDITIONAL_INFORMATION_LABEL": "Additional Information", - "MEDICATION_DRUG_NAME_PLACEHOLDER": "Choose Drug", - "MEDICATION_CHANGE_DURATION_BUTTON": "Change Duration", - "MEDICATION_NEW_PRESCRIPTION": "New Prescription", - "MEDICATION_CLEAR": "Clear", - "MEDICATION_ADD_ACCESS_KEY": "a", - "MEDICATION_REFILL_ALL": "Refill All", - "MEDICATION_NO_RECENT_TREATMENT": "No Recent Treatments", - "MEDICATION_STOP": "Stop", - "MEDICATION_REFILL": "Refill", - "MEDICATION_STARTED_ON": "Started on", - "MEDICATION_STOPPED_ON": "Stopped on", - "MEDICATION_DUE_TO": "due to", - "MEDICATION_LABEL_START_DATE": "Start Date", - "REMOVE_CONFIRMATION_KEY": "Are you sure you want to remove?", - "CLEAR_CONFIRMATION_KEY": "Are you sure you want to clear?", - "REMOVE_DRUG_ORDER_KEY": "Are you sure you want to remove drug : {{ drugName }} ?", - "ENTER_PATIENT_WEIGHT_ERROR": "Patient weight has not been captured recently. Prescription cannot be created.", - "ENTER_DIAGNOSIS_ERROR": "Primary diagnosis has not been captured. Prescription cannot be created", - "PATIENT_WEIGHT_AND_DIAGNOSIS_ERROR": "Patient weight has not been captured recently & no primary diagnosis is found. Prescription cannot be created", + "CONSULTATION_TAB_KEY": "Consultation", + "CONSULTATION_SHORTCUT_KEY": "c", + "NO_ACTIVE_PASSIVE_PROGRAMS_MESSAGE": "No active/inactive programs for this patient.", + "NO_TREATMENTS_MESSAGE": "No treatments for this patient.", + "NO_ADMISSION_DETAILS_MESSAGE": "No Admission Details for this patient.", + "NO_DOCUMENTS_MESSAGE": "No documents for this patient.", + "NO_DISPOSTIONS_AVAILABLE_MESSAGE_KEY": "No dispositions available.", + "NO_LAB_ORDERS_FOR_PATIENT_MESSAGE_KEY": "No Lab Orders for this patient.", + "DISPLAY_CONTROL_ERROR_MESSAGE_KEY": "No {{ displayControl }} recorded for this patient", + "NO_DIAGNOSIS_MESSAGE": "No diagnosis for this patient", + "NO_SPECIMEN_RESULT_FOUND_MESSAGE": "No Result found for this specimen", + "NO_SPECIMEN_FOUND_MESSAGE": "No specimen for this patient", + "NO_DATA_MESSAGE": "No data captured.", + "NO_DRUG_DETAILS_MESSAGE": "No Drugs to show for this patient", + "DOB_LABEL": "Date of Birth", + "ALL_LAB_RESULTS_LABEL": "All Lab Results", + "ACCESSION_AT_LABEL": "Accession at", + "SAMPLE_AT_LABEL": "Sample at", + "PROGRAM_MANAGEMENT_SELECT_PROGRAM_KEY": "New Program Enrollment", + "PROGRAM_MANAGEMENT_ACTIVE_PROGRAM_KEY": "Active Programs", + "PROGRAM_MANAGEMENT_INACTIVE_PROGRAM_KEY": "InActive Programs", + "PROGRAM_MANAGEMENT_ACTIVE_TREATMENT_KEY": "Active Treatments", + "PROGRAM_MANAGEMENT_INACTIVE_TREATMENT_KEY": "InActive Treatments", + "PROGRAM_MANAGEMENT_PAST_PROGRAM_KEY": "Past Programs", + "PROGRAM_MANAGEMENT_PROGRAM_STATES_KEY": "Program States", + "PROGRAM_MANAGEMENT_PROGRAM_ATTRIBUTES_KEY": "Program Attributes", + "PROGRAM_MANAGEMENT_LABEL_PROGRAM": "Program", + "PROGRAM_MANAGEMENT_CHOOSE_PROGRAM_KEY": "Choose Program", + "PROGRAM_MANAGEMENT_LABEL_PROGRAM_STATE": "Program State", + "PROGRAM_MANAGEMENT_LABEL_PROGRAM_OUTCOME": "Program Outcome", + "PROGRAM_MANAGEMENT_LABEL_CHOOSE_OUTCOME": "Choose Outcome", + "PROGRAM_MANAGEMENT_CHOOSE_STATE_KEY": "Choose Program State", + "PROGRAM_MANAGEMENT_CHANGE_STATE_KEY": "Change Program State", + "PROGRAM_MANAGEMENT_START_DATE_KEY": "Start Date", + "PROGRAM_MANAGEMENT_STOP_DATE_KEY": "Stop Date", + "PROGRAM_MANAGEMENT_STARTED_ON_KEY": "Started on", + "PROGRAM_MANAGEMENT_SELECT_PROGRAM_MESSAGE_KEY": "Please select a Program to Enroll the patient", + "PROGRAM_MANAGEMENT_ALREADY_ENROLLED_PROGRAM_MESSAGE_KEY": "Patient already enrolled to the Program", + "PROGRAM_MANAGEMENT_STATE_CANT_START_BEFORE_KEY": "State cannot be started earlier than current state", + "PROGRAM_MANAGEMENT_PROGRAM_CANT_END_BEFORE_KEY": "Program cannot be ended earlier than current state", + "PROGRAM_MANAGEMENT_DO_YOU_WANT_TO_REMOVE_KEY": "Do you want to remove", + "PATIENT_NOT_ENROLLED_TO_PROGRAMS_MESSAGE_KEY": "The Patient has not enrolled to any programs", + "PATIENT_DOESNT_HAVE_PREVIOUS_PROGRAMS_MESSAGE_KEY": "The Patient doesn't have any previous programs", + "PROGRAM_MANAGEMENT_ENROLL_KEY": "Enroll", + "PROGRAM_MANAGEMENT_TO_KEY": "to", + "PROGRAM_MANAGEMENT_ON_KEY": "on", + "PROGRAM_MANAGEMENT_BY_KEY": "By", + "PROGRAM_MANAGEMENT_End_PROGRAM_KEY": "End Program", + "PROGRAM_MANAGEMENT_DETAILS_KEY": "Details", + "PROGRAM_MANAGEMENT_GENERAL_CONSULTATION_KEY": "General Consultation", + "PROGRAM_DISPLAY_CONTROL_TREATMENT_STATES_KEY": "Treatment States", + "PROGRAM_DISPLAY_CONTROL_TREATMENT_START_DATE_KEY": "Treatment Start Date", + "PROGRAM_DISPLAY_CONTROL_TREATMENT_STOP_DATE_KEY": "Treatment Stop Date", + "PROGRAM_DISPLAY_CONTROL_FACILITY_OF_ENROLLMENT_KEY": "Facility of Enrollment", + "TREATMENTS_SUMMARY_PRESCRIPTION_KEY": "Prescriptions", + "INVESTIGATION_CHART_KEY": "Investigation Chart", + "LAB_ORDERS_KEY": "Lab Orders", + "ALL_VISITS_KEY": "All Visits", + "ALL_DIAGNOSES_KEY": "All Diagnoses", + "ALL_KEY": "All", + "SEARCH_NAME_PLACEHOLDER_KEY": "Search Name/{{ primaryIdentifier }} ...", + "SEARCH_KEY": "Search", + "CLINICAL_YEARS_TRANSLATION_KEY": "years", + "CLINICAL_MONTHS_TRANSLATION_KEY": "months", + "CLINICAL_DAYS_TRANSLATION_KEY": "days", + "CONCEPT_SET_GROUP_EXPAND_ALL_KEY": "Expand All", + "CONCEPT_SET_GROUP_COLLAPSE_ALL_KEY": "Collapse All", + "ADD_NEW_OBSERVATION": "Add New Obs Form", + "CONCEPT_SET_GROUP_PIN_UNPIN_KEY": "Pin/Unpin", + "CONCEPT_SET_GROUP_REMOVE_KEY": "Remove", + "CONCEPT_SET_GROUP_SEARCH_LABEL": "Search Obs Form", + "EMPTY_TEMPLATE_MESSAGE": "Click on 'Add New Obs Form' button to add new forms here", + "SCROLL_LEFT": "Scroll to Left", + "SCROLL_RIGHT": "Scroll to Right", + "VISIT_TITLE_PATIENT_INFORMATION": "PatientInformation", + "VISIT_TITLE_DIAGNOSIS_KEY": "Diagnoses", + "VISIT_TITLE_DISPOSITIONS_KEY": "Dispositions", + "VISIT_TITLE_DISPOSITION_KEY": "Disposition", + "VISIT_TITLE_ADMISSION_DETAILS_KEY": "Admission Details", + "VISIT_TITLE_LAB_INVESTIGATIONS_KEY": "Lab Investigations", + "VISIT_TITLE_TREATMENTS_KEY": "Treatments", + "VISIT_TITLE_PATIENT_FILES_KEY": "Patient Files", + "VISIT_TITLE_RADIOLOGY_DOCUMENTS_KEY": "Radiology Documents", + "VISIT_TITLE_OBSERVATIONS_KEY": "Observations", + "ADD_MORE_KEY": "Add More", + "ADD_SECTION_KEY": "Add Section", + "ADD_FORM_KEY": "Add Form", + "CLINICAL_SAVE_TRANSLATION_KEY": "Save", + "CLINICAL_SAVE_SUCCESS_MESSAGE_KEY": "Saved", + "CLINICAL_SAVE_FAILURE_MESSAGE_KEY": "An error has occurred on the server. Information not saved.", + "CLINICAL_FORM_ERRORS_MESSAGE_KEY": "Please enter a value in the mandatory fields or correct the value in the highlighted fields to proceed", + "CLINICAL_FORM_EDIT_ERROR_MESSAGE_KEY": "Form not found", + "CLINICAL_ORDER_RADIOLOGY_NEED_PRINT": "Need Print for this order.", + "CLINICAL_ORDER_RADIOLOGY_NEED_PRINT_BUTTON": "Needs Print", + "CLINICAL_DECIMAL_FAILURE_MESSAGE_KEY": "Please enter Integer value, decimal value is not allowed", + "SAVE_ACCESS_KEY": "s", + "CLINICAL_PRINT_TRANSLATION_KEY": "Print", + "CLINICAL_PRINT_ACCESS_KEY": "r", + "CLINICAL_SHARE_PRESCRIPTION_KEY": "Share or print the prescription", + "VISIT_ON_TRANSLATION_KEY": "Visit On", + "ADD_TO_DRUG_CHART_KEY": "Add to Drug Chart", + "IPD_BUTTON": "IPD", + "ENTERING_DATA_FOR_TRANSLATION_KEY": "Entering data for", + "EDITITNG_DATA_FOR_ENCOUNTER_TRANSLATION_KEY": "Editing data for encounter", + "ON_BEHALF_OF_TRANSLATION_KEY": "On behalf of", + "PATIENT_DOCUMENT_TRANSLATION_KEY": "Patient Documents", + "BAHMNI_HOME_TRANSLATION_KEY": "Bahmni Home", + "PATIENT_QUEUE_TRANSLATION_KEY": "Patient Queue", + "LOGOUT_TRANSLATION_KEY": "Logout", + "OBS_ABNORMAL_TRANSLATE_KEY": "Abnormal", + "OBS_UNKNOWN_TRANSLATE_KEY": "Unknown", + "LAB_INVESTIGATIONS_KEY": "Lab Investigations", + "BACTERIOLOGY_SAMPLE_COLLECTION_DATE_KEY": "Date of Sample Collection", + "BACTERIOLOGY_SAMPLE_TYPE_KEY": "Type of sample", + "BACTERIOLOGY_SAMPLE_ID_KEY": "Sample Id", + "BACTERIOLOGY_TAB_TITLE_KEY": "Sample Details", + "BACTERIOLOGY_BUTTON_ADD_SAMPLE_KEY": "ADD SAMPLE", + "BACTERIOLOGY_BUTTON_CLEAR_KEY": "Clear", + "BACTERIOLOGY_BUTTON_EDIT_KEY": "Edit", + "BACTERIOLOGY_ADDED_ON_KEY": "Added on", + "BACTERIOLOGY_SAMPLE_TYPE_KEY_NON_CODED": "Other Sample Type", + "MEDICATION_RECENT_TAB": "Recent", + "MEDICATION_RECENT_TAB_STOP_REASON": "Stop Reason", + "MEDICATION_RECENT_TAB_STOP_NOTES": "Notes", + "MEDICATION_TAB_DOSE": "Dose", + "MEDICATION_DEFAULT_DRUGS_TITLE": "Frequently Used", + "MEDICATION_DRUG_NAME_TITLE": "Drug Name", + "MEDICATION_ADD_DRUG_FORM_TITLE": "Order Drug", + "MEDICATION_ADD_ORDERSET_FORM_TITLE": "Order an Order Set", + "MEDICATION_LABEL_DOSE": "Dose", + "MEDICATION_LABEL_RULE": "Rule", + "MEDICATION_CHOOSE_RULE": "Choose Rule", + "MEDICATION_LABEL_UNITS": "Units", + "MEDICATION_LABEL_FREQUENCY": "Frequency", + "MEDICATION_LABEL_ROUTE": "Route", + "MEDICATION_LABEL_DURATION": "Duration", + "MEDICATION_LABEL_TOTAL_QUANTITY": "Total Quantity", + "MEDICATION_LABEL_AS_NEEDED": "As Needed", + "MEDICATION_DOSE_UNIT_PLACEHOLDER": "Choose Unit", + "MEDICATION_FREQUENCY_UNIT_PLACEHOLDER": "Choose Frequency", + "MEDICATION_ROUTE_PLACEHOLDER": "Choose Route", + "MEDICATION_CHOOSE_UNIT_PLACEHOLDER": "Choose Unit", + "MEDICATION_CHOOSE_QUANTITY_UNIT_PLACEHOLDER": "Choose Unit", + "MEDICATION_AS_NEEDED": "SOS", + "MEDICATION_AS_NEEDED_BUTTON_PLACEHOLDER": "SOS", + "MEDICATION_AS_NEEDED_ACCESS_KEY": "o", + "MEDICATION_LABEL_DOSING_INSTRUCTIONS": "Instructions", + "MEDICATION_DOSING_INSTRUCTIONS_PLACEHOLDER": "Choose Instruction", + "MEDICATION_LABEL_ADDITIONAL_INSTRUCTIONS": "Additional Instructions", + "MEDICATION_ADDITIONAL_INFORMATION_LABEL": "Additional Information", + "MEDICATION_DRUG_NAME_PLACEHOLDER": "Choose Drug", + "MEDICATION_CHANGE_DURATION_BUTTON": "Change Duration", + "MEDICATION_NEW_PRESCRIPTION": "New Prescription", + "MEDICATION_CLEAR": "Clear", + "MEDICATION_ADD_ACCESS_KEY": "a", + "MEDICATION_REFILL_ALL": "Refill All", + "MEDICATION_NO_RECENT_TREATMENT": "No Recent Treatments", + "MEDICATION_STOP": "Stop", + "MEDICATION_REFILL": "Refill", + "MEDICATION_STARTED_ON": "Started on", + "MEDICATION_STOPPED_ON": "Stopped on", + "MEDICATION_DUE_TO": "due to", + "MEDICATION_LABEL_START_DATE": "Start Date", + "REMOVE_CONFIRMATION_KEY": "Are you sure you want to remove?", + "CLEAR_CONFIRMATION_KEY": "Are you sure you want to clear?", + "REMOVE_DRUG_ORDER_KEY": "Are you sure you want to remove drug : {{ drugName }} ?", + "ENTER_PATIENT_WEIGHT_ERROR": "Patient weight has not been captured recently. Prescription cannot be created.", + "ENTER_DIAGNOSIS_ERROR": "Primary diagnosis has not been captured. Prescription cannot be created", + "PATIENT_WEIGHT_AND_DIAGNOSIS_ERROR": "Patient weight has not been captured recently & no primary diagnosis is found. Prescription cannot be created", - "DRUG_DETAILS_DRUG_NAME": "Drug", - "DRUG_DETAILS_DOSE_INFO": "Dose", - "DRUG_DETAILS_ROUTE": "Route", - "DRUG_DETAILS_FREQUENCY": "Schedule", - "DRUG_DETAILS_START_DATE": "Start date", - "DRUG_DETAILS_STOP_DATE": "Stop date", - "DRUG_DETAILS_ORDER_REASON_CODED": "Stop Reason", - "DRUG_DETAILS_INSTRUCTIONS_TEXT": "Instructions", - "DRUG_DETAILS_QUANTITY_TEXT": "Quantity", - "DRUG_DETAILS_DURATION": "Duration", - "DRUG_DETAILS_ADDITIONAL_INSTRUCTIONS": "Additional instructions", - "MONTHS": "January, February, March, April, May, June, July, August, September, October, November, December", - "CHOOSE_MONTH_KEY": "Choose month", - "CHOOSE_YEAR_KEY": "Choose year", - "HOME_DASHBOARD_KEY": "Home", - "PATIENT_VISIT_PAGE_KEY": "Visit", - "PATIENT_ADT_PAGE_KEY": "Inpatient", - "HEADER_LABEL_SYNC_KEY": "Sync", - "PROGRAM_MANAGEMENT_PAGE_KEY": "Enrollment", - "PATIENT_VISIT_ATTRIBUTES_PAGE_KEY": "Visit Attributes", - "PATIENT_REGISTRATION_PAGE_KEY": "Registration", - "SAVE_CONFIRMATION_DIALOG_MESSAGE_KEY": "You may have unsaved changes, please choose an option.", - "SAVE_CONFIRMATION_OPTION_DONT_SAVE_KEY": "Don't Save", - "SAVE_CONFIRMATION_OPTION_SAVE_KEY": "Save", - "SAVE_CONFIRMATION_OPTION_CANCEL_KEY": "Cancel", - "BROWSER_CLOSE_DIALOG_MESSAGE_KEY": "You might lose unsaved data", - "POP_UP_CLOSE_DIALOG_MESSAGE_KEY": "You might lose unsaved data. Are you sure you want to leave this page?", - "ALERT_MESSAGE_ON_EXIT": "There is unsaved consultation data for this patient! Do you wish to Review, or Discard the data?", - "MESSAGE_DIALOG_OPTION_COPY": "Copy Error", - "MESSAGE_DIALOG_OPTION_OKAY": "OK", - "MESSAGE_DIALOG_OPTION_REVIEW": "Review", - "MESSAGE_DIALOG_OPTION_DISCARD": "Discard", - "LAB_ENTRY_KEY": "Lab Entry", - "OBS_BOOLEAN_YES_KEY": "Yes", - "OBS_BOOLEAN_NO_KEY": "No", - "CLINICAL_DUPLICATE_DIAGNOSIS_ERROR_MESSAGE": "Please correct the duplicate diagnosis entered.", - "CLINICAL_TEMPLATE_ADDED_SUCCESS_KEY": "{{label}} added successfully", - "CLINICAL_TEMPLATE_REMOVED_SUCCESS_KEY": "{{label}} removed successfully", - "CLINICAL_DIAGNOSIS_PRIMARY": "PRIMARY", - "CLINICAL_DIAGNOSIS_SECONDARY": "SECONDARY", - "CLINICAL_DIAGNOSIS_CONFIRMED": "CONFIRMED", - "CLINICAL_DIAGNOSIS_PRESUMED": "PRESUMED", - "CLINICAL_DIAGNOSIS_ORDER_PRIMARY": "PRIMARY", - "CLINICAL_DIAGNOSIS_ORDER_SECONDARY": "SECONDARY", - "CLINICAL_DIAGNOSIS_CERTAINTY_CONFIRMED": "CONFIRMED", - "CLINICAL_DIAGNOSIS_CERTAINTY_PRESUMED": "PRESUMED", - "ACCEPT": "Accept", - "CLINICAL_ACTION": "Action", - "CLINICAL_DIAGNOSIS": "Diagnoses", - "CLINICAL_ORDER": "Order", - "CLINICAL_CERTAINTY": "Certainty", - "CLINICAL_STATUS": "Status", - "CLINICAL_CURRENT": "Current", - "CLINICAL_PAST_DIAGNOSIS": "Past Diagnoses", - "CLINICAL_INITIAL": "Initial", - "CLINICAL_CHANGED": "Changed", - "CLINICAL_UPDATE": "Update", - "CANCEL": "Cancel", - "CLINICAL_ADD_AS_CONDITION": "Add as condition", - "CONDITION_LIST_DISPLAY_CONTROL_TITLE": "Conditions", - "CONDITION_LIST_ACTIVE": "Active", - "CONDITION_LIST_INACTIVE": "Inactive", - "CONDITION_LIST_HISTORY_OF": "History Of", - "CONDITION_LIST_ADD": "Add", - "CONDITION_LIST_CONDITION": "Condition", - "CONDITION_LIST_STATUS": "Status", - "CONDITION_LIST_DATE": "Date", - "CONDITION_LIST_NOTES": "Notes", - "CONDITION_LIST_CONDITIONS_ACTIVE": "Active Conditions", - "CONDITION_LIST_CONDITIONS_HISTORY_OF": "History of Conditions", - "CONDITION_LIST_CONDITIONS_INACTIVE": "Inactive Conditions", - "CONDITION_LIST_SET_AS_INACTIVE": "Inactive", - "CONDITION_LIST_SET_AS_HISTORY_OF": "History of", - "CONDITION_LIST_ALREADY_EXISTS": "Condition already exists with status '{{status}}' in conditions list({{lastActive}})", - "CONDITION_LIST_ALREADY_EXISTS_AS_ACTIVE": "Already exists as active condition", - "CONDITION_LIST_CREATED_BY": "by", - "CONDITION_LIST_FOLLOW_UP": "Follow up", - "CONDITION_LIST_ACTIVE_SINCE": "condition from", - "CONDITION_LIST_NO_CONDITIONS": "No Conditions available", - "CONDITION_LIST_NO_RETRO_MODE": "Conditions cannot be edited in retrospective mode", - "SEARCH_BY_PATIENT_ID_KEY": "Search Patient by ID", - "RECENT_PATIENTS_LABEL": "Recent Patients", - "OPEN_IN_NEW_WINDOW": "Open in new window", - "NO_RESULTS_FOUND": "No results found", - "CONTROL_PANEL_ACTIONS": "Actions", - "CONTROL_PANEL_OTHER_ACTIONS": "Other Actions", - "CONTROL_PANEL_CONSULTATION_TEXT": "Consultation", - "CONTROL_PANEL_DASHBOARD_TEXT": "Dashboard", - "PRIVILEGE_REQUIRED": "User is logged in but doesn't have the relevant privilege", - "NO_FULFILMENT_MESSAGE": "No observations captured for this order.", - "DISCONTINUING_AND_ORDERING_SAME_DRUG_NOT_ALLOWED": "Discontinuing and ordering the same drug is not allowed. Instead, use edit", - "INCOMPLETE_FORM_ERROR_MESSAGE": "Please click on Add or Clear to continue", - "CONCEPT_NOT_NUMERIC": "Concept ':conceptName''s datatype is not Numeric. At :placeErrorAccurred.", - "NO_OBSERVATIONS_CAPTURED": "No observations captured for this visit.", - "NO_NAVIGATION_LINKS_AVAILABLE_MESSAGE": "No navigation links available.", - "NO_LAB_ORDERS_MESSAGE": "No lab orders.", - "NO_ACTIVE_VISIT_MESSAGE": "No active visit.", - "MEDICATION_NO_PRESCRIPTION_SAVED_PLACEHOLDER": "No prescriptions saved yet", - "TREATMENT_NOTES": "Treatment Notes", - "MEDICATION_CLEAR_BUTTON_LABEL": "Clear", - "MEDICATION_ADD_BUTTON_LABEL": "Add", - "MEDICATION_DOSAGE_INFORMATION_LABEL": "Dosage Instructions", - "TREATMENT_SL_NO": "S. No.", - "SELECT_VALUE_FROM_AUTOCOMPLETE_DEFAULT_MESSAGE": "Please select a value from auto complete", - "RETROSPECTIVE_POPUP_LOCATION_LABEL": "Location", - "RETROSPECTIVE_POPUP_ON_BEHALF_LABEL": "Enter on behalf", - "RETROSPECTIVE_POPUP_DATA_FOR_LABEL": "Enter data for", - "OKAY_LABEL": "OK", - "ORDER_CAN_NOT_EDIT_AFTER_SAVE_MESSAGE": "Can't edit the notes after saving.", - "ENTER_ORDER_NOTE_LABEL": "Enter notes for", - "BACTERIOLOGY_NOT_ALLOWED_IN_RETROSPECTIVE_MESSAGE": "Bacteriology entry is NOT allowed in retrospective mode", - "ORDER_NOT_ALLOWED_IN_RETROSPECTIVE_MESSAGE": "Order entry is NOT allowed in retrospective mode", - "DISPOSITION_NOT_ALLOWED_IN_RETROSPECTIVE_MESSAGE": "Disposition entry is NOT allowed in retrospective mode", - "DISPOSITION_TYPE_LABEL": "Disposition Type", - "DISPOSITION_NOTE_LABEL": "Disposition Notes", - "DISPOSITION_NOTE_WILL_BE_REMOVED_WARNING_MESSAGE": "The Disposition Note will also be removed", - "CLINICAL_ADD_NOTES": "Add Notes", - "INVESTIGATION_NO_TEST_PRESENT_MESSAGE": "There are no tests. Please setup tests", - "CONSULTATION_TREATMENTS_HEADER_LABEL": "Treatments", - "MEDICATION_DRUG_NAME": "Drug Name", - "MEDICATION_DOSAGE_INSTRUCTIONS": "Dosage Instructions", - "MEDICATION_NEW_TREATMENTS": "New Treatments", - "MEDICATION_DISCONTINUED_TREATMENTS": "Discontinued Treatments", - "CONSULTATION_PAD_EMPTY_MESSAGE": "Consultation pad is empty", - "CONSULTATION_TAB_DIAGNOSES_HEADER_LABEL": "Diagnoses", - "CONSULTATION_TAB_OBSERVATIONS_HEADER_LABEL": "Observations", - "CONSULTATION_TAB_INVESTIGATION_HEADER_LABEL": "Investigation", - "CONSULTATION_TAB_DISPOSITION_HEADER_LABEL": "Disposition", - "CONSULTATION_TAB_NEW_DIAGNOSES_LABEL": "Newly Added", - "CONSULTATION_TAB_REVISED_DIAGNOSES_LABEL": "Revised", - "CONSULTATION_TAB_DIAGNOSES_STATUS_LABEL": "Status", - "CONSULTATION_TAB_PAST_DIAGNOSES_LABEL": "Past", - "CONSULTATION_TAB_DIAGNOSES_NOTES_LABEL": "Diagnosis Notes", - "CONSULTATION_TAB_DISPOSITIONS_NOTES_LABEL": "Disposition Notes", - "CONSULTATION_TAB_CONSULTATION_NOTES_LABEL": "Consultation Notes", - "NOTES_LABEL": "Notes", - "SELECTED_ORDERS_LABEL": "Selected Orders", - "SELECTED_ORDERS_EMPTY_MESSAGE": "Selected orders is empty", - "DISEASE_NO_DATA_MESSAGE": "   No data", - "OBSERVATION_GRAPH_NO_DATA_LABEL": "No Data", - "TREATMENT_CHART_HEADER_LABEL": "Treatment Chart", - "CLINICAL_ENTER_NOTES_PLACEHOLDER": "Enter Notes here", - "CONSULTATION_ON_KEY": "on", - "CONCEPT_NOT_FOUND_MESSAGE": "concept not found.", - "NO_OBS_FOUND_MESSAGE": "No observations made for this patient.", - "ADMISSION_DATE_LABEL": "Admission Date", - "DISCHARGE_DATE_LABEL": "Discharge Date", - "MONTH_LABEL": "Month", - "MEDICATIONS_ACTIONS_LABEL": "Actions", - "MEDICATIONS_STOP_LABEL": "Stop", - "MEDICATIONS_REFILL_LABEL": "Refill", - "TIME_OF_BIRTH_LABEL": "Time of Birth", - "PATIENT_PROFILE_RELATIONSHIPS_LABEL": "Relationships", - "BROWSER_DOES_NOT_SUPPORT_VIDEO_TAG_MESSAGE": "Your browser does not support the video tag.", - "YEARS_LABEL": "Years", - "TAKE_PATIENT_PHOTO_TITTLE": "Take Patient's Photo", - "CLICK_PATIENT_PHOTO_LABEL": "Click Photo", - "CONFIRM_PATIENT_PHOTO_LABEL": "Confirm Photo", - "CHOOSE_ANSWER_FROM_DROPDOWN_LABEL": "Choose Answer", - "PROGRAM_DELETE_REASON_PLACEHOLDER": "Reason to delete the program", - "DELETE_LABEL": "Delete", - "EDIT_LABEL": "Edit", - "INVESTIGATIONS_PANELS_HEADER_LABEL": "Panels", - "INVESTIGATIONS_SELECTIONS_HEADER_LABEL": "Selection", - "CLINICAL_GO_TO_DASHBOARD_LABEL": "Go to IPD dashboard", - "NO_FORM_FOUND_FOR_PATIENT": "No Form found for this patient", - "CLINICAL_ACCEPT": "ACCEPT", - "PROGRAM_HIV_PROGRAM": "HIV Program", - "PROGRAM_TB_PPROGRAM": "TB Program", - "PROGRAM_ID_NUMBER": "ID Number", - "PROGRAM_TREATMENT_DATE": "Treatment Date", - "PROGRAM_COMORBIDITES": "Co-morbidites", - "PROGRAM_DOCTORINCHARGE": "Doctor In-Charge", - "PROGRAM_PATIENT_STAGE": "Patient Stage", - "CLINICAL_CLASS": "Class", - "CLINICAL_CASTE": "Caste", - "NO_PACS_STUDY_FOR_ORDER": "Unable to identify study for order. Searching with accession number.", - "RADIOLOGY_STUDY_FETCH_ERROR": "Unable to fetch radiology studies. Please verify PACS configuration", - "NO_IMAGE_YET_FOR_ORDER": "No image available yet for order", - "ENTER_IMPRESSION_ON_IMAGE": "Enter Impression on Image", - "CLOSE_TAB_MESSAGE": "Please close this tab.", - "NO_ACTIVE_VISIT_FOUND_MESSAGE": "No Active visit found for this patient.", - "DELETED_MESSAGE": "Deleted", - "UPDATE_DURATION_OF_THE_DRUGS_MESSAGE": "There are drugs that do not have a duration specified. Updating duration will update for those drugs as well", - "INCORRECT_CONFIGURATION_MESSAGE": "Incorrect Configuration:", - "NEW_KEY": "A new", - "SECTION_ADDED_KEY": "section has been added", - "FILE_TYPE_NOT_SUPPORTED_MESSAGE": "File type is not supported", - "NO_IMAGE_AVAILABLE_FOR_ORDER_MESSAGE": "No image available yet for order:", - "ADD_KEY": "Add", - "USE_KEY": "Use", - "ENTER_NOTES_MESSAGE": "Please enter your notes here...", - "DURATION_KEY": "Duration", - "SAVE_KEY": "Save", - "START_TELECON_KEY": "Start Teleconsultation", - "TELECON_ERROR_KEY": "Error while fetching teleconsultation link", - "CHIEF_COMPLAINT_DATA_CONCEPT_NAME_KEY": "Chief Complaint Data", - "CHIEF_COMPLAINT_MESSAGE_KEY": "{{ChiefComplaintCoded}} {{Chiefcomplainttext}} since {{Signsymptomduration}} {{ChiefComplaintDuration}}", - "SHARE_PRESCRIPTION_MAIL_CONTENT": "Hi #recipientName,\nThanks for visiting #locationName on #visitDate. Your prescription is attached with this email. Wish you a speedy recovery.\nThanks.\n#locationName\n#locationAddress", - "CHIEF_COMPLAINT_CODED_KEY": "Chief Complaint Coded", - "SIGN_SYMPTOM_DURATION_KEY": "Sign/symptom duration", - "CHIEF_COMPLAINT_DURATION_UNIT_KEY": "Chief Complaint Duration", - "DASHBOARD_IPD_DRUG_CHART_TAB_KEY": "Drug Chart", - "FALSE_POSITIVE_ALERT": "The alert triggered, but it was a false positive and no action is needed.", - "NOT_APPLICABLE_TO_THE_PATIENT": "The alert does not apply to the patient in question.", - "ALREADY_RESOLVED_BY_MEDICATION_ADJUSTMENT": "The alert was previously resolved by adjusting the patient's medication.", - "ALREADY_MANAGED_BY_OTHER_MEANS": "The alert was previously managed by other means, and no further action is needed.", - "PATIENT_DECLINED_RECOMMENDED_ACTION": "The patient declined the recommended action despite the alert.", - "CLINICIAN_JUDGMENT_OVERRODE_RECOMMENDATION": "The clinician used their judgment to override the recommendation generated by the alert.", - "ALERT_TRIGGERED_IN_ERROR": "The alert triggered in error, and no action is needed.", - "INSUFFICIENT_INFORMATION_TO_ACT_ON_ALERT": "There is not enough information available to act on the alert.", - "CDSS_WARNING": "Warning", - "CDSS_CRITICAL": "Critical", - "CDSS_ALERT_DISMISSAL_REASON": "Select reason for dismissal", - "CDSS_INFO": "Info", - "CDSS_ALERT_DISMISS": "Dismiss", - "DISMISSED_KEY": "Dismissed", - "CDSS_ALERT_SAVE_ERROR": "A CDSS Critical Alert needs to be addressed in Medications Tab before continuing with Save operation", - "CDSS_ACTION": "Please go to Medications Tab to take further action" + "DRUG_DETAILS_DRUG_NAME": "Drug", + "DRUG_DETAILS_DOSE_INFO": "Dose", + "DRUG_DETAILS_ROUTE": "Route", + "DRUG_DETAILS_FREQUENCY": "Schedule", + "DRUG_DETAILS_START_DATE": "Start date", + "DRUG_DETAILS_STOP_DATE": "Stop date", + "DRUG_DETAILS_ORDER_REASON_CODED": "Stop Reason", + "DRUG_DETAILS_INSTRUCTIONS_TEXT": "Instructions", + "DRUG_DETAILS_QUANTITY_TEXT": "Quantity", + "DRUG_DETAILS_DURATION": "Duration", + "DRUG_DETAILS_ADDITIONAL_INSTRUCTIONS": "Additional instructions", + "MONTHS": "January, February, March, April, May, June, July, August, September, October, November, December", + "CHOOSE_MONTH_KEY": "Choose month", + "CHOOSE_YEAR_KEY": "Choose year", + "HOME_DASHBOARD_KEY": "Home", + "PATIENT_VISIT_PAGE_KEY": "Visit", + "PATIENT_ADT_PAGE_KEY": "Inpatient", + "HEADER_LABEL_SYNC_KEY": "Sync", + "PROGRAM_MANAGEMENT_PAGE_KEY": "Enrollment", + "PATIENT_VISIT_ATTRIBUTES_PAGE_KEY": "Visit Attributes", + "PATIENT_REGISTRATION_PAGE_KEY": "Registration", + "SAVE_CONFIRMATION_DIALOG_MESSAGE_KEY": "You may have unsaved changes, please choose an option.", + "SAVE_CONFIRMATION_OPTION_DONT_SAVE_KEY": "Don't Save", + "SAVE_CONFIRMATION_OPTION_SAVE_KEY": "Save", + "SAVE_CONFIRMATION_OPTION_CANCEL_KEY": "Cancel", + "BROWSER_CLOSE_DIALOG_MESSAGE_KEY": "You might lose unsaved data", + "POP_UP_CLOSE_DIALOG_MESSAGE_KEY": "You might lose unsaved data. Are you sure you want to leave this page?", + "ALERT_MESSAGE_ON_EXIT": "There is unsaved consultation data for this patient! Do you wish to Review, or Discard the data?", + "MESSAGE_DIALOG_OPTION_COPY": "Copy Error", + "MESSAGE_DIALOG_OPTION_OKAY": "OK", + "MESSAGE_DIALOG_OPTION_REVIEW": "Review", + "MESSAGE_DIALOG_OPTION_DISCARD": "Discard", + "LAB_ENTRY_KEY": "Lab Entry", + "OBS_BOOLEAN_YES_KEY": "Yes", + "OBS_BOOLEAN_NO_KEY": "No", + "CLINICAL_DUPLICATE_DIAGNOSIS_ERROR_MESSAGE": "Please correct the duplicate diagnosis entered.", + "CLINICAL_TEMPLATE_ADDED_SUCCESS_KEY": "{{label}} added successfully", + "CLINICAL_TEMPLATE_REMOVED_SUCCESS_KEY": "{{label}} removed successfully", + "CLINICAL_DIAGNOSIS_PRIMARY": "PRIMARY", + "CLINICAL_DIAGNOSIS_SECONDARY": "SECONDARY", + "CLINICAL_DIAGNOSIS_CONFIRMED": "CONFIRMED", + "CLINICAL_DIAGNOSIS_PRESUMED": "PRESUMED", + "CLINICAL_DIAGNOSIS_ORDER_PRIMARY": "PRIMARY", + "CLINICAL_DIAGNOSIS_ORDER_SECONDARY": "SECONDARY", + "CLINICAL_DIAGNOSIS_CERTAINTY_CONFIRMED": "CONFIRMED", + "CLINICAL_DIAGNOSIS_CERTAINTY_PRESUMED": "PRESUMED", + "ACCEPT": "Accept", + "CLINICAL_ACTION": "Action", + "CLINICAL_DIAGNOSIS": "Diagnoses", + "CLINICAL_ORDER": "Order", + "CLINICAL_CERTAINTY": "Certainty", + "CLINICAL_STATUS": "Status", + "CLINICAL_CURRENT": "Current", + "CLINICAL_PAST_DIAGNOSIS": "Past Diagnoses", + "CLINICAL_INITIAL": "Initial", + "CLINICAL_CHANGED": "Changed", + "CLINICAL_UPDATE": "Update", + "CANCEL": "Cancel", + "CLINICAL_ADD_AS_CONDITION": "Add as condition", + "CONDITION_LIST_DISPLAY_CONTROL_TITLE": "Conditions", + "CONDITION_LIST_ACTIVE": "Active", + "CONDITION_LIST_INACTIVE": "Inactive", + "CONDITION_LIST_HISTORY_OF": "History Of", + "CONDITION_LIST_ADD": "Add", + "CONDITION_LIST_CONDITION": "Condition", + "CONDITION_LIST_STATUS": "Status", + "CONDITION_LIST_DATE": "Date", + "CONDITION_LIST_NOTES": "Notes", + "CONDITION_LIST_CONDITIONS_ACTIVE": "Active Conditions", + "CONDITION_LIST_CONDITIONS_HISTORY_OF": "History of Conditions", + "CONDITION_LIST_CONDITIONS_INACTIVE": "Inactive Conditions", + "CONDITION_LIST_SET_AS_INACTIVE": "Inactive", + "CONDITION_LIST_SET_AS_HISTORY_OF": "History of", + "CONDITION_LIST_ALREADY_EXISTS": "Condition already exists with status '{{status}}' in conditions list({{lastActive}})", + "CONDITION_LIST_ALREADY_EXISTS_AS_ACTIVE": "Already exists as active condition", + "CONDITION_LIST_CREATED_BY": "by", + "CONDITION_LIST_FOLLOW_UP": "Follow up", + "CONDITION_LIST_ACTIVE_SINCE": "condition from", + "CONDITION_LIST_NO_CONDITIONS": "No Conditions available", + "CONDITION_LIST_NO_RETRO_MODE": "Conditions cannot be edited in retrospective mode", + "SEARCH_BY_PATIENT_ID_KEY": "Search Patient by ID", + "RECENT_PATIENTS_LABEL": "Recent Patients", + "OPEN_IN_NEW_WINDOW": "Open in new window", + "NO_RESULTS_FOUND": "No results found", + "CONTROL_PANEL_ACTIONS": "Actions", + "CONTROL_PANEL_OTHER_ACTIONS": "Other Actions", + "CONTROL_PANEL_CONSULTATION_TEXT": "Consultation", + "CONTROL_PANEL_DASHBOARD_TEXT": "Dashboard", + "PRIVILEGE_REQUIRED": "User is logged in but doesn't have the relevant privilege", + "NO_FULFILMENT_MESSAGE": "No observations captured for this order.", + "DISCONTINUING_AND_ORDERING_SAME_DRUG_NOT_ALLOWED": "Discontinuing and ordering the same drug is not allowed. Instead, use edit", + "INCOMPLETE_FORM_ERROR_MESSAGE": "Please click on Add or Clear to continue", + "CONCEPT_NOT_NUMERIC": "Concept ':conceptName''s datatype is not Numeric. At :placeErrorAccurred.", + "NO_OBSERVATIONS_CAPTURED": "No observations captured for this visit.", + "NO_NAVIGATION_LINKS_AVAILABLE_MESSAGE": "No navigation links available.", + "NO_LAB_ORDERS_MESSAGE": "No lab orders.", + "NO_ACTIVE_VISIT_MESSAGE": "No active visit.", + "MEDICATION_NO_PRESCRIPTION_SAVED_PLACEHOLDER": "No prescriptions saved yet", + "TREATMENT_NOTES": "Treatment Notes", + "MEDICATION_CLEAR_BUTTON_LABEL": "Clear", + "MEDICATION_ADD_BUTTON_LABEL": "Add", + "MEDICATION_DOSAGE_INFORMATION_LABEL": "Dosage Instructions", + "TREATMENT_SL_NO": "S. No.", + "SELECT_VALUE_FROM_AUTOCOMPLETE_DEFAULT_MESSAGE": "Please select a value from auto complete", + "RETROSPECTIVE_POPUP_LOCATION_LABEL": "Location", + "RETROSPECTIVE_POPUP_ON_BEHALF_LABEL": "Enter on behalf", + "RETROSPECTIVE_POPUP_DATA_FOR_LABEL": "Enter data for", + "OKAY_LABEL": "OK", + "ORDER_CAN_NOT_EDIT_AFTER_SAVE_MESSAGE": "Can't edit the notes after saving.", + "ENTER_ORDER_NOTE_LABEL": "Enter notes for", + "BACTERIOLOGY_NOT_ALLOWED_IN_RETROSPECTIVE_MESSAGE": "Bacteriology entry is NOT allowed in retrospective mode", + "ORDER_NOT_ALLOWED_IN_RETROSPECTIVE_MESSAGE": "Order entry is NOT allowed in retrospective mode", + "DISPOSITION_NOT_ALLOWED_IN_RETROSPECTIVE_MESSAGE": "Disposition entry is NOT allowed in retrospective mode", + "DISPOSITION_TYPE_LABEL": "Disposition Type", + "DISPOSITION_NOTE_LABEL": "Disposition Notes", + "DISPOSITION_NOTE_WILL_BE_REMOVED_WARNING_MESSAGE": "The Disposition Note will also be removed", + "CLINICAL_ADD_NOTES": "Add Notes", + "INVESTIGATION_NO_TEST_PRESENT_MESSAGE": "There are no tests. Please setup tests", + "CONSULTATION_TREATMENTS_HEADER_LABEL": "Treatments", + "MEDICATION_DRUG_NAME": "Drug Name", + "MEDICATION_DOSAGE_INSTRUCTIONS": "Dosage Instructions", + "MEDICATION_NEW_TREATMENTS": "New Treatments", + "MEDICATION_DISCONTINUED_TREATMENTS": "Discontinued Treatments", + "CONSULTATION_PAD_EMPTY_MESSAGE": "Consultation pad is empty", + "CONSULTATION_TAB_DIAGNOSES_HEADER_LABEL": "Diagnoses", + "CONSULTATION_TAB_OBSERVATIONS_HEADER_LABEL": "Observations", + "CONSULTATION_TAB_INVESTIGATION_HEADER_LABEL": "Investigation", + "CONSULTATION_TAB_DISPOSITION_HEADER_LABEL": "Disposition", + "CONSULTATION_TAB_NEW_DIAGNOSES_LABEL": "Newly Added", + "CONSULTATION_TAB_REVISED_DIAGNOSES_LABEL": "Revised", + "CONSULTATION_TAB_DIAGNOSES_STATUS_LABEL": "Status", + "CONSULTATION_TAB_PAST_DIAGNOSES_LABEL": "Past", + "CONSULTATION_TAB_DIAGNOSES_NOTES_LABEL": "Diagnosis Notes", + "CONSULTATION_TAB_DISPOSITIONS_NOTES_LABEL": "Disposition Notes", + "CONSULTATION_TAB_CONSULTATION_NOTES_LABEL": "Consultation Notes", + "NOTES_LABEL": "Notes", + "SELECTED_ORDERS_LABEL": "Selected Orders", + "SELECTED_ORDERS_EMPTY_MESSAGE": "Selected orders is empty", + "DISEASE_NO_DATA_MESSAGE": "   No data", + "OBSERVATION_GRAPH_NO_DATA_LABEL": "No Data", + "TREATMENT_CHART_HEADER_LABEL": "Treatment Chart", + "CLINICAL_ENTER_NOTES_PLACEHOLDER": "Enter Notes here", + "CONSULTATION_ON_KEY": "on", + "CONCEPT_NOT_FOUND_MESSAGE": "concept not found.", + "NO_OBS_FOUND_MESSAGE": "No observations made for this patient.", + "ADMISSION_DATE_LABEL": "Admission Date", + "DISCHARGE_DATE_LABEL": "Discharge Date", + "MONTH_LABEL": "Month", + "MEDICATIONS_ACTIONS_LABEL": "Actions", + "MEDICATIONS_STOP_LABEL": "Stop", + "MEDICATIONS_REFILL_LABEL": "Refill", + "TIME_OF_BIRTH_LABEL": "Time of Birth", + "PATIENT_PROFILE_RELATIONSHIPS_LABEL": "Relationships", + "BROWSER_DOES_NOT_SUPPORT_VIDEO_TAG_MESSAGE": "Your browser does not support the video tag.", + "YEARS_LABEL": "Years", + "TAKE_PATIENT_PHOTO_TITTLE": "Take Patient's Photo", + "CLICK_PATIENT_PHOTO_LABEL": "Click Photo", + "CONFIRM_PATIENT_PHOTO_LABEL": "Confirm Photo", + "CHOOSE_ANSWER_FROM_DROPDOWN_LABEL": "Choose Answer", + "PROGRAM_DELETE_REASON_PLACEHOLDER": "Reason to delete the program", + "DELETE_LABEL": "Delete", + "EDIT_LABEL": "Edit", + "INVESTIGATIONS_PANELS_HEADER_LABEL": "Panels", + "INVESTIGATIONS_SELECTIONS_HEADER_LABEL": "Selection", + "CLINICAL_GO_TO_DASHBOARD_LABEL": "Go to IPD dashboard", + "NO_FORM_FOUND_FOR_PATIENT": "No Form found for this patient", + "CLINICAL_ACCEPT": "ACCEPT", + "PROGRAM_HIV_PROGRAM": "HIV Program", + "PROGRAM_TB_PPROGRAM": "TB Program", + "PROGRAM_ID_NUMBER": "ID Number", + "PROGRAM_TREATMENT_DATE": "Treatment Date", + "PROGRAM_COMORBIDITES": "Co-morbidites", + "PROGRAM_DOCTORINCHARGE": "Doctor In-Charge", + "PROGRAM_PATIENT_STAGE": "Patient Stage", + "CLINICAL_CLASS": "Class", + "CLINICAL_CASTE": "Caste", + "NO_PACS_STUDY_FOR_ORDER": "Unable to identify study for order. Searching with accession number.", + "RADIOLOGY_STUDY_FETCH_ERROR": "Unable to fetch radiology studies. Please verify PACS configuration", + "NO_IMAGE_YET_FOR_ORDER": "No image available yet for order", + "ENTER_IMPRESSION_ON_IMAGE": "Enter Impression on Image", + "CLOSE_TAB_MESSAGE": "Please close this tab.", + "NO_ACTIVE_VISIT_FOUND_MESSAGE": "No Active visit found for this patient.", + "DELETED_MESSAGE": "Deleted", + "UPDATE_DURATION_OF_THE_DRUGS_MESSAGE": "There are drugs that do not have a duration specified. Updating duration will update for those drugs as well", + "INCORRECT_CONFIGURATION_MESSAGE": "Incorrect Configuration:", + "NEW_KEY": "A new", + "SECTION_ADDED_KEY": "section has been added", + "FILE_TYPE_NOT_SUPPORTED_MESSAGE": "File type is not supported", + "NO_IMAGE_AVAILABLE_FOR_ORDER_MESSAGE": "No image available yet for order:", + "ADD_KEY": "Add", + "USE_KEY": "Use", + "ENTER_NOTES_MESSAGE": "Please enter your notes here...", + "DURATION_KEY": "Duration", + "SAVE_KEY": "Save", + "START_TELECON_KEY": "Start Teleconsultation", + "TELECON_ERROR_KEY": "Error while fetching teleconsultation link", + "CHIEF_COMPLAINT_DATA_CONCEPT_NAME_KEY": "Chief Complaint Data", + "CHIEF_COMPLAINT_MESSAGE_KEY": "{{ChiefComplaintCoded}} {{Chiefcomplainttext}} since {{Signsymptomduration}} {{ChiefComplaintDuration}}", + "SHARE_PRESCRIPTION_MAIL_CONTENT": "Hi #recipientName,\nThanks for visiting #locationName on #visitDate. Your prescription is attached with this email. Wish you a speedy recovery.\nThanks.\n#locationName\n#locationAddress", + "CHIEF_COMPLAINT_CODED_KEY": "Chief Complaint Coded", + "SIGN_SYMPTOM_DURATION_KEY": "Sign/symptom duration", + "CHIEF_COMPLAINT_DURATION_UNIT_KEY": "Chief Complaint Duration", + "DASHBOARD_IPD_DRUG_CHART_TAB_KEY": "Drug Chart", + "FALSE_POSITIVE_ALERT": "The alert triggered, but it was a false positive and no action is needed.", + "NOT_APPLICABLE_TO_THE_PATIENT": "The alert does not apply to the patient in question.", + "ALREADY_RESOLVED_BY_MEDICATION_ADJUSTMENT": "The alert was previously resolved by adjusting the patient's medication.", + "ALREADY_MANAGED_BY_OTHER_MEANS": "The alert was previously managed by other means, and no further action is needed.", + "PATIENT_DECLINED_RECOMMENDED_ACTION": "The patient declined the recommended action despite the alert.", + "CLINICIAN_JUDGMENT_OVERRODE_RECOMMENDATION": "The clinician used their judgment to override the recommendation generated by the alert.", + "ALERT_TRIGGERED_IN_ERROR": "The alert triggered in error, and no action is needed.", + "INSUFFICIENT_INFORMATION_TO_ACT_ON_ALERT": "There is not enough information available to act on the alert.", + "CDSS_WARNING": "Warning", + "CDSS_CRITICAL": "Critical", + "CDSS_ALERT_DISMISSAL_REASON": "Select reason for dismissal", + "CDSS_INFO": "Info", + "CDSS_ALERT_DISMISS": "Dismiss", + "DISMISSED_KEY": "Dismissed", + "CDSS_ALERT_SAVE_ERROR": "A CDSS Critical Alert needs to be addressed in Medications Tab before continuing with Save operation", + "CDSS_ACTION": "Please go to Medications Tab to take further action", + "MEDICATION_PRINT": "Print" } diff --git a/ui/app/i18n/registration/locale_en.json b/ui/app/i18n/registration/locale_en.json index 97385aaa6e..20688dc432 100644 --- a/ui/app/i18n/registration/locale_en.json +++ b/ui/app/i18n/registration/locale_en.json @@ -124,4 +124,4 @@ "PATIENT_ATTRIBUTE_MIDDLE_NAME_LOCAL": "Middle Name in Arabic", "RELATED_PATIENT_IDENTIFIER_LABEL": "Patient", "REGISTRATION_LABEL_SAVE_REDIRECTION": "Saved and Visit Started Successfully" -} \ No newline at end of file +} diff --git a/ui/app/styles/clinical/treatment/_treatment.scss b/ui/app/styles/clinical/treatment/_treatment.scss index 3c326e359a..1b598a6c15 100644 --- a/ui/app/styles/clinical/treatment/_treatment.scss +++ b/ui/app/styles/clinical/treatment/_treatment.scss @@ -133,7 +133,7 @@ @media screen and (min-width:769px) { float: right; - width: 215px; + width: 255px; } button.toggle.has-notes { @@ -285,7 +285,8 @@ } .refill-drugs-btn, - .dispense-all-btn { + .dispense-all-btn, + .print-drugs-btn { font-size: 14px; margin-right: 0; display: none; @@ -304,6 +305,10 @@ margin-right: 5px; } + .print-drugs-btn { + margin-left: 5px; + } + .bulk-edit-options-container { padding: 10px 10px 10px 10px; background-color: $lightestGray; @@ -341,7 +346,8 @@ } .tabs .tab [type=radio]:checked~.dispense-all-btn, - .tabs .tab [type=radio]:checked~.refill-drugs-btn { + .tabs .tab [type=radio]:checked~.refill-drugs-btn, + .tabs .tab [type=radio]:checked~.print-drugs-btn { display: inline-block; line-height: 14px; } diff --git a/ui/app/styles/common/_bahmniGlobal.scss b/ui/app/styles/common/_bahmniGlobal.scss index 72c2af5f55..3db5ce504d 100644 --- a/ui/app/styles/common/_bahmniGlobal.scss +++ b/ui/app/styles/common/_bahmniGlobal.scss @@ -131,7 +131,7 @@ input[file-upload] { } button.save-consultation { float: right; - margin: 8px 0 0 10px; + margin: 8px 10px 0; padding: 10px 15px 9px; } button.save-consultation { diff --git a/ui/test/unit/clinical/controllers/drugOrderHistoryController.spec.js b/ui/test/unit/clinical/controllers/drugOrderHistoryController.spec.js index f9f416c3cc..74d1684157 100644 --- a/ui/test/unit/clinical/controllers/drugOrderHistoryController.spec.js +++ b/ui/test/unit/clinical/controllers/drugOrderHistoryController.spec.js @@ -4,7 +4,7 @@ describe("DrugOrderHistoryController", function () { beforeEach(module('bahmni.clinical')); var scope, prescribedDrugOrders, activeDrugOrder, _treatmentService, - retrospectiveEntryService, appService, rootScope, visitHistory; + retrospectiveEntryService, appService, rootScope, visitHistory, allergyService; var DateUtil = Bahmni.Common.Util.DateUtil; var treatmentConfig = { drugOrderHistoryConfig: { @@ -50,6 +50,13 @@ describe("DrugOrderHistoryController", function () { retrospectiveEntryService.getRetrospectiveEntry.and.returnValue(retrospectiveEntry); spinner = jasmine.createSpyObj('spinner', ['forPromise']); visitHistory = {}; + appService = jasmine.createSpyObj('appService', ['getAppDescriptor']); + appService.getAppDescriptor.and.returnValue({ + getConfigValue: function (config) { + return false; + } + }); + allergyService = jasmine.createSpyObj('allergyService', ['getAllergyForPatient']); })); var initController = function () { @@ -64,7 +71,8 @@ describe("DrugOrderHistoryController", function () { spinner: spinner, visitHistory: visitHistory, treatmentConfig: treatmentConfig, - appService: appService + appService: appService, + allergyService: allergyService }); rootScope.$apply(); }; @@ -93,6 +101,26 @@ describe("DrugOrderHistoryController", function () { it("should get prescribed and active Drugorders with correct no of visits ", function () { expect(_treatmentService.getPrescribedDrugOrders).toHaveBeenCalledWith("patientUuid", true, 4, undefined, undefined); }); + it("should selectAllDrugs for print", function () { + translate.instant.and.returnValue("Recent"); + initController(); + expect(scope.consultation.drugOrderGroups.length).toBe(3); + + scope.selectAllDrugs(scope.consultation.drugOrderGroups[0], 0); + expect(Object.keys(scope.selectedDrugs).length).toBe(3); + expect() + }) + it("should not selectAllDrugs for print when auto select is not allowed", function () { + translate.instant.and.returnValue("Recent"); + initController(); + expect(scope.consultation.drugOrderGroups.length).toBe(3); + + scope.autoSelectNotAllowed = true + + scope.selectAllDrugs(scope.consultation.drugOrderGroups[0], 0); + expect(Object.keys(scope.selectedDrugs).length).toBe(0); + expect() + }) }); describe("when conditionally enable or disable order reason text for drug stoppage", function () { diff --git a/ui/test/unit/clinical/common/services/allergyService.spec.js b/ui/test/unit/common/services/allergyService.spec.js similarity index 99% rename from ui/test/unit/clinical/common/services/allergyService.spec.js rename to ui/test/unit/common/services/allergyService.spec.js index ee91aaaaa5..a94d8df948 100644 --- a/ui/test/unit/clinical/common/services/allergyService.spec.js +++ b/ui/test/unit/common/services/allergyService.spec.js @@ -1,7 +1,7 @@ describe('allergyService', function() { var _$http, appService; - beforeEach(module('bahmni.clinical')); + beforeEach(module('bahmni.common.util')); beforeEach(module(function () { _$http = jasmine.createSpyObj('$http', ['get', 'delete']); From 997873410b90aea92c4a4e84f87bfb3ff6721e86 Mon Sep 17 00:00:00 2001 From: Himabindu T Date: Thu, 30 May 2024 10:24:07 +0530 Subject: [PATCH 12/24] BAH-3850 | Add. Care View Dashboard Into IPD And Bed Mgmt * Arjun | BAH-3850 | To build a page in React for new Ward-level patients dashboard (#793) * add. changes to view Care View Dashboard * add. care view dashboard * add. callback to jump back to in-patient screen * add. callback to jump back to in-patient screen * [Sri] - add provider uuid in care view dashboard * Phani | A-1206676106718218 | Update CSS to make Ward level Dashboard responsive * Phani | A-1206857808595293 | Add new route for Care View Dashboard (#878) * Phani | Add new state and route for Care View Dashboard * Phani | Add controller for careViewDashboard * Phani | Add initialization to careViewDashboard (#880) * Phani | Add new state and route for Care View Dashboard * Phani | Add controller for careViewDashboard * Phani | Add initialization for careViewDashboard * update. PR Feedback comments * [Rahul] | BAH-3850 | Add. Care View Support In Bed Mgmt * [Rahul] | BAH-3850 | Add. Custom Loader * BAH-3850 | auditLog events for Care view and IPD Dashboard --------- Co-authored-by: srinithishg Co-authored-by: Phanindra-tw Co-authored-by: Arjun-Go Co-authored-by: Rahul Ramesh --- micro-frontends/src/ipd/CareViewDashboard.jsx | 25 ++++ micro-frontends/src/ipd/IpdDashboard.jsx | 27 +++++ micro-frontends/src/ipd/index.js | 47 ++++++++ .../src/next-ui/Components/Loader/Loader.jsx | 35 ++++++ .../src/next-ui/Components/Loader/Loader.scss | 109 ++++++++++++++++++ .../next-ui/Components/Loader/Loader.spec.jsx | 30 +++++ .../Loader/__snapshots__/Loader.spec.jsx.snap | 33 ++++++ micro-frontends/webpack.config.js | 7 +- ui/app/adt/app.js | 18 ++- ui/app/adt/controllers/careViewController.js | 34 ++++++ ui/app/adt/index.html | 17 ++- ui/app/adt/init.js | 2 +- ui/app/adt/views/home.html | 6 +- ui/app/adt/views/wardList.html | 16 +-- ui/app/bedmanagement/app.js | 17 ++- .../controllers/careViewController.js | 34 ++++++ ui/app/bedmanagement/index.html | 13 +++ ui/app/bedmanagement/init.js | 2 +- ui/app/bedmanagement/views/home.html | 21 ++-- ui/app/common/models/auditLogEventDetails.js | 11 +- ui/app/i18n/adt/locale_en.json | 5 +- ui/app/i18n/ipd/locale_en.json | 3 +- ui/app/styles/adt/_adt.scss | 1 + .../controllers/careViewController.spec.js | 85 ++++++++++++++ .../controllers/careViewController.spec.js | 85 ++++++++++++++ 25 files changed, 656 insertions(+), 27 deletions(-) create mode 100644 micro-frontends/src/ipd/CareViewDashboard.jsx create mode 100644 micro-frontends/src/ipd/IpdDashboard.jsx create mode 100644 micro-frontends/src/ipd/index.js create mode 100644 micro-frontends/src/next-ui/Components/Loader/Loader.jsx create mode 100644 micro-frontends/src/next-ui/Components/Loader/Loader.scss create mode 100644 micro-frontends/src/next-ui/Components/Loader/Loader.spec.jsx create mode 100644 micro-frontends/src/next-ui/Components/Loader/__snapshots__/Loader.spec.jsx.snap create mode 100644 ui/app/adt/controllers/careViewController.js create mode 100644 ui/app/bedmanagement/controllers/careViewController.js create mode 100644 ui/test/unit/adt/controllers/careViewController.spec.js create mode 100644 ui/test/unit/bedmanagement/controllers/careViewController.spec.js diff --git a/micro-frontends/src/ipd/CareViewDashboard.jsx b/micro-frontends/src/ipd/CareViewDashboard.jsx new file mode 100644 index 0000000000..3b793ed3a1 --- /dev/null +++ b/micro-frontends/src/ipd/CareViewDashboard.jsx @@ -0,0 +1,25 @@ +import PropTypes from "prop-types"; +import React, { Suspense, lazy } from "react"; +import Loader from "../next-ui/Components/Loader/Loader"; + +export function CareViewDashboard(props) { + const LazyApp = lazy(() => import("@openmrs-mf/ipd/CareViewDashboard")); + + return ( + <> + }> + + + + ); +} + +// Without propTypes, react2angular won't render the component +CareViewDashboard.propTypes = { + hostData: PropTypes.shape({ + patientId: PropTypes.string, + }).isRequired, + hostApi: PropTypes.shape({ + onHome: PropTypes.func, + }).isRequired, +}; \ No newline at end of file diff --git a/micro-frontends/src/ipd/IpdDashboard.jsx b/micro-frontends/src/ipd/IpdDashboard.jsx new file mode 100644 index 0000000000..3b5bfefcfc --- /dev/null +++ b/micro-frontends/src/ipd/IpdDashboard.jsx @@ -0,0 +1,27 @@ +import PropTypes from "prop-types"; +import React, { Suspense, lazy } from "react"; + +export function IpdDashboard(props) { + const LazyApp = lazy(() => import("@openmrs-mf/ipd/IpdDashboard")); + + return ( + <> + }> + + + + ); +} + +// Without propTypes, react2angular won't render the component +IpdDashboard.propTypes = { + hostData: PropTypes.object.isRequired, + hostApi: PropTypes.shape({ + navigation: PropTypes.shape({ + visitSummary: PropTypes.func.isRequired, + }).isRequired, + }).isRequired, +}; diff --git a/micro-frontends/src/ipd/index.js b/micro-frontends/src/ipd/index.js new file mode 100644 index 0000000000..cd74ca75b8 --- /dev/null +++ b/micro-frontends/src/ipd/index.js @@ -0,0 +1,47 @@ +// Adapt all IPD components to use within angular +"use strict"; + +import { react2angular } from "react2angular"; +import { IpdDashboard } from "./IpdDashboard"; +// import { DrugChartDashboard } from "./DrugChartDasboard"; +import { CareViewDashboard } from "./CareViewDashboard"; + +angular.module("bahmni.mfe.ipd", [ + "ui.router", + "bahmni.common.config", + "bahmni.common.uiHelper", + "bahmni.common.i18n", + "bahmni.common.domain", +]); + +/** MFE component 1: IpdDashboard + *================================================= */ + +angular + .module("bahmni.mfe.ipd") + .component("mfeIpdDashboard", react2angular(IpdDashboard), { + template: + '' + }); + + +/** MFE component 2: DrugChartDashboard + *================================================= */ + +// angular +// .module("bahmni.mfe.ipd") +// .component("mfeDrugChartDashboard", react2angular(DrugChartDashboard), { +// template: +// '' +// }); + + +/** MFE component 3: CareViewDashboard + *================================================= */ + +angular +.module("bahmni.mfe.ipd") +.component("mfeIpdCareViewDashboard", react2angular(CareViewDashboard), { + template: + '' +}); \ No newline at end of file diff --git a/micro-frontends/src/next-ui/Components/Loader/Loader.jsx b/micro-frontends/src/next-ui/Components/Loader/Loader.jsx new file mode 100644 index 0000000000..9f2f0747ff --- /dev/null +++ b/micro-frontends/src/next-ui/Components/Loader/Loader.jsx @@ -0,0 +1,35 @@ +import React from 'react'; +import { FormattedMessage } from "react-intl"; +import './Loader.scss'; + +const Loader = () => { + const loadingMessage = ( + + ); + return ( +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

{loadingMessage}

+
+
+ ); +}; + +export default Loader; diff --git a/micro-frontends/src/next-ui/Components/Loader/Loader.scss b/micro-frontends/src/next-ui/Components/Loader/Loader.scss new file mode 100644 index 0000000000..785ba9e037 --- /dev/null +++ b/micro-frontends/src/next-ui/Components/Loader/Loader.scss @@ -0,0 +1,109 @@ +.lds-spinner, +.lds-spinner div, +.lds-spinner div:after { + box-sizing: border-box; +} +.lds-spinner { + color: #719898; + display: inline-block; + position: relative; + width: 80px; + height: 80px; + + div { + transform-origin: 40px 40px; + animation: lds-spinner 1.2s linear infinite; + + &:after { + content: " "; + display: block; + position: absolute; + top: 3.2px; + left: 36.8px; + width: 6.4px; + height: 17.6px; + border-radius: 20%; + background: #719898; + } + + &:nth-child(1) { + transform: rotate(0deg); + animation-delay: -1.1s; + } + &:nth-child(2) { + transform: rotate(30deg); + animation-delay: -1s; + } + &:nth-child(3) { + transform: rotate(60deg); + animation-delay: -0.9s; + } + &:nth-child(4) { + transform: rotate(90deg); + animation-delay: -0.8s; + } + &:nth-child(5) { + transform: rotate(120deg); + animation-delay: -0.7s; + } + &:nth-child(6) { + transform: rotate(150deg); + animation-delay: -0.6s; + } + &:nth-child(7) { + transform: rotate(180deg); + animation-delay: -0.5s; + } + &:nth-child(8) { + transform: rotate(210deg); + animation-delay: -0.4s; + } + &:nth-child(9) { + transform: rotate(240deg); + animation-delay: -0.3s; + } + &:nth-child(10) { + transform: rotate(270deg); + animation-delay: -0.2s; + } + &:nth-child(11) { + transform: rotate(300deg); + animation-delay: -0.1s; + } + &:nth-child(12) { + transform: rotate(330deg); + animation-delay: 0s; + } + } +} + +@keyframes lds-spinner { + 0% { + opacity: 1; + } + 100% { + opacity: 0; + } +} + +.loader-container { + font-family: "Arial"; + display: flex; + align-items: center; + justify-content: center; + height: 80vh; + width: 100vw; + background-color: #fafafa; +} + +.loader-content { + display: flex; + flex-direction: column; + align-items: center; +} + +.loader-text { + font-size: 16px; + color: #333; + margin: 15px 0 0; +} diff --git a/micro-frontends/src/next-ui/Components/Loader/Loader.spec.jsx b/micro-frontends/src/next-ui/Components/Loader/Loader.spec.jsx new file mode 100644 index 0000000000..674cde64e0 --- /dev/null +++ b/micro-frontends/src/next-ui/Components/Loader/Loader.spec.jsx @@ -0,0 +1,30 @@ +import React from 'react'; +import { render } from '@testing-library/react'; +import { IntlProvider } from 'react-intl'; +import renderer from 'react-test-renderer'; +import Loader from './Loader'; + +const messages = { + LOADING_MESSAGE: 'Loading... Please Wait' +}; + +describe('Loader Component', () => { + it('renders loader correctly', () => { + const { getAllByText } = render( + + + + ); + + expect(getAllByText("Loading... Please Wait").length).toEqual(1); + }); + + it('matches snapshot', () => { + const tree = renderer.create( + + + + ).toJSON(); + expect(tree).toMatchSnapshot(); + }); +}); diff --git a/micro-frontends/src/next-ui/Components/Loader/__snapshots__/Loader.spec.jsx.snap b/micro-frontends/src/next-ui/Components/Loader/__snapshots__/Loader.spec.jsx.snap new file mode 100644 index 0000000000..3e7016726f --- /dev/null +++ b/micro-frontends/src/next-ui/Components/Loader/__snapshots__/Loader.spec.jsx.snap @@ -0,0 +1,33 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Loader Component matches snapshot 1`] = ` +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+ Loading... Please Wait +

+
+
+`; diff --git a/micro-frontends/webpack.config.js b/micro-frontends/webpack.config.js index 84a8fab58d..cfc7308d20 100644 --- a/micro-frontends/webpack.config.js +++ b/micro-frontends/webpack.config.js @@ -14,6 +14,7 @@ module.exports = { }, }, entry: { + ipd: "./src/ipd/index.js", "next-ui": "./src/next-ui/index.js", shared: "./src/shared.js", }, @@ -33,7 +34,9 @@ module.exports = { new ModuleFederationPlugin({ name: "bahmni_mfe_host", filename: "remoteEntry.js", - remotes: {}, + remotes: { + "@openmrs-mf/ipd": remoteProxiedAtHostDomain({ name: "bahmni_ipd", path: "ipd" }), + }, exposes: {}, shared: { "carbon-components-react": { @@ -133,4 +136,4 @@ function remoteProxiedAtHostDomain({name, path}) { // inject this script with the src set to the resolved remoteEntry.js document.head.appendChild(script); })`; -} \ No newline at end of file +} diff --git a/ui/app/adt/app.js b/ui/app/adt/app.js index b5c26bbbbd..91edb89f82 100644 --- a/ui/app/adt/app.js +++ b/ui/app/adt/app.js @@ -28,8 +28,12 @@ angular.module('adt').config(['$stateProvider', '$httpProvider', '$urlRouterProv views: { 'content': { templateUrl: 'views/home.html', - controller: function ($scope, appService) { + controller: function ($rootScope, $scope, appService, $state) { + $scope.goToCareView = function () { + $state.go('careViewDashboard'); + }; $scope.isBedManagementEnabled = appService.getAppDescriptor().getConfig("isBedManagementEnabled").value; + $scope.enableIPDFeature = appService.getAppDescriptor().getConfigValue('enableIPDFeature'); } }, 'wards@home': { @@ -44,6 +48,18 @@ angular.module('adt').config(['$stateProvider', '$httpProvider', '$urlRouterProv initialization: 'initialization' } }) + .state('careViewDashboard', { + url: '/home/careViewDashboard', + views: { + 'content': { + template: '', + controller: 'CareViewController' + } + }, + resolve: { + initialization: 'initialization' + } + }) .state('patient', { url: '/patient/:patientUuid', data: { diff --git a/ui/app/adt/controllers/careViewController.js b/ui/app/adt/controllers/careViewController.js new file mode 100644 index 0000000000..2a8bb41750 --- /dev/null +++ b/ui/app/adt/controllers/careViewController.js @@ -0,0 +1,34 @@ +"use strict"; + +angular.module('bahmni.adt') +.controller('CareViewController', ['$rootScope', '$scope', '$state', '$window', 'auditLogService', 'sessionService', function ($rootScope, $scope, $state, $window, auditLogService, sessionService) { + function handleLogoutShortcut (event) { + if ((event.metaKey || event.ctrlKey) && event.key === $rootScope.quickLogoutComboKey) { + $scope.hostApi.onLogOut(); + } + } + function cleanup () { + $window.removeEventListener('keydown', handleLogoutShortcut); + } + $window.addEventListener('keydown', handleLogoutShortcut); + $scope.$on('$destroy', cleanup); + $scope.hostData = { + provider: $rootScope.currentProvider + }; + $scope.hostApi = { + onHome: function () { + $state.go('home'); + }, + onLogOut: function () { + auditLogService.log(undefined, 'USER_LOGOUT_SUCCESS', undefined, 'MODULE_LABEL_LOGOUT_KEY').then(function () { + sessionService.destroy().then( + function () { + $window.location = "../home/index.html#/login"; + }); + }); + }, + handleAuditEvent: function (patientUuid, eventType, messageParams, module) { + return auditLogService.log(patientUuid, eventType, messageParams, module); + } + }; +}]); diff --git a/ui/app/adt/index.html b/ui/app/adt/index.html index bea5b4b6e3..9916b91fab 100644 --- a/ui/app/adt/index.html +++ b/ui/app/adt/index.html @@ -2,6 +2,7 @@ + @@ -11,8 +12,11 @@ + + + - ADT + IPD
@@ -53,6 +57,16 @@ + + + + + + + + + + @@ -237,6 +251,7 @@ + diff --git a/ui/app/adt/init.js b/ui/app/adt/init.js index ab2cd09df2..c6718b2789 100644 --- a/ui/app/adt/init.js +++ b/ui/app/adt/init.js @@ -3,5 +3,5 @@ var Bahmni = Bahmni || {}; Bahmni.ADT = Bahmni.ADT || {}; -angular.module('bahmni.adt', ['bahmni.common.conceptSet', 'bahmni.common.logging']); +angular.module('bahmni.adt', ['bahmni.common.conceptSet', 'bahmni.common.logging', 'bahmni.mfe.ipd']); diff --git a/ui/app/adt/views/home.html b/ui/app/adt/views/home.html index 5960f37b2d..34be89c80d 100644 --- a/ui/app/adt/views/home.html +++ b/ui/app/adt/views/home.html @@ -1,3 +1,7 @@ + + {{ 'CARE_VIEW_LABEL'|translate }} + + @@ -7,4 +11,4 @@ {{ 'WARD_LIST_LABEL'|translate }}
-
\ No newline at end of file +
diff --git a/ui/app/adt/views/wardList.html b/ui/app/adt/views/wardList.html index 9670c9cb11..cda0727431 100644 --- a/ui/app/adt/views/wardList.html +++ b/ui/app/adt/views/wardList.html @@ -3,14 +3,15 @@ {{ 'EMPTY_WARD_MESSAGE'|translate }} - - +
+
+ - - - + + + - -
{{heading | titleTranslate}}
{{row[heading]}} @@ -31,6 +32,7 @@
+ + +
diff --git a/ui/app/bedmanagement/app.js b/ui/app/bedmanagement/app.js index 59bddbe61b..c3520c4468 100644 --- a/ui/app/bedmanagement/app.js +++ b/ui/app/bedmanagement/app.js @@ -32,8 +32,12 @@ angular.module('ipd').config(['$stateProvider', '$httpProvider', '$urlRouterProv views: { 'content': { templateUrl: 'views/home.html', - controller: function ($scope, appService) { + controller: function ($scope, appService, $state) { + $scope.goToCareView = function () { + $state.go('careViewDashboard'); + }; $scope.isBedManagementEnabled = appService.getAppDescriptor().getConfig("isBedManagementEnabled").value; + $scope.enableIPDFeature = appService.getAppDescriptor().getConfigValue('enableIPDFeature'); } }, 'additional-header': { @@ -126,6 +130,17 @@ angular.module('ipd').config(['$stateProvider', '$httpProvider', '$urlRouterProv }); } } + }).state('careViewDashboard', { + url: '/home/careViewDashboard', + views: { + 'content': { + template: '', + controller: 'CareViewController' + } + }, + resolve: { + initialization: 'initialization' + } }); $bahmniTranslateProvider.init({app: 'ipd', shouldMerge: true}); diff --git a/ui/app/bedmanagement/controllers/careViewController.js b/ui/app/bedmanagement/controllers/careViewController.js new file mode 100644 index 0000000000..e2f2d664d9 --- /dev/null +++ b/ui/app/bedmanagement/controllers/careViewController.js @@ -0,0 +1,34 @@ +"use strict"; + +angular.module('bahmni.ipd') +.controller('CareViewController', ['$rootScope', '$scope', '$state', '$window', 'auditLogService', 'sessionService', function ($rootScope, $scope, $state, $window, auditLogService, sessionService) { + function handleLogoutShortcut (event) { + if ((event.metaKey || event.ctrlKey) && event.key === $rootScope.quickLogoutComboKey) { + $scope.hostApi.onLogOut(); + } + } + function cleanup () { + $window.removeEventListener('keydown', handleLogoutShortcut); + } + $window.addEventListener('keydown', handleLogoutShortcut); + $scope.$on('$destroy', cleanup); + $scope.hostData = { + provider: $rootScope.currentProvider + }; + $scope.hostApi = { + onHome: function () { + $state.go('home'); + }, + onLogOut: function () { + auditLogService.log(undefined, 'USER_LOGOUT_SUCCESS', undefined, 'MODULE_LABEL_LOGOUT_KEY').then(function () { + sessionService.destroy().then( + function () { + $window.location = "../home/index.html#/login"; + }); + }); + }, + handleAuditEvent: function (patientUuid, eventType, messageParams, module) { + return auditLogService.log(patientUuid, eventType, messageParams, module); + } + }; +}]); diff --git a/ui/app/bedmanagement/index.html b/ui/app/bedmanagement/index.html index c1f615deb3..ff99ee3e33 100644 --- a/ui/app/bedmanagement/index.html +++ b/ui/app/bedmanagement/index.html @@ -12,6 +12,9 @@ + + + IPD @@ -54,7 +57,15 @@ + + + + + + + + @@ -223,6 +234,7 @@ + @@ -234,6 +246,7 @@ + diff --git a/ui/app/bedmanagement/init.js b/ui/app/bedmanagement/init.js index 4a5c5a23ec..a704db6a14 100644 --- a/ui/app/bedmanagement/init.js +++ b/ui/app/bedmanagement/init.js @@ -3,4 +3,4 @@ var Bahmni = Bahmni || {}; Bahmni.IPD = Bahmni.IPD || {}; -angular.module('bahmni.ipd', ['bahmni.common.conceptSet', 'bahmni.common.logging']); +angular.module('bahmni.ipd', ['bahmni.common.conceptSet', 'bahmni.common.logging', 'bahmni.mfe.ipd']); diff --git a/ui/app/bedmanagement/views/home.html b/ui/app/bedmanagement/views/home.html index f467e1d386..0c649b8d17 100644 --- a/ui/app/bedmanagement/views/home.html +++ b/ui/app/bedmanagement/views/home.html @@ -1,10 +1,15 @@ - - -
- - - - - + \ No newline at end of file diff --git a/ui/app/common/models/auditLogEventDetails.js b/ui/app/common/models/auditLogEventDetails.js index 0beb07acd4..93428f6542 100644 --- a/ui/app/common/models/auditLogEventDetails.js +++ b/ui/app/common/models/auditLogEventDetails.js @@ -32,5 +32,14 @@ Bahmni.Common.AuditLogEventDetails = { "VIEWED_DASHBOARD_OBSERVATION": {eventType: "VIEWED_DASHBOARD_OBSERVATION", message: "VIEWED_DASHBOARD_OBSERVATION_MESSAGE"}, "VIEWED_PATIENTPROGRAM": {eventType: "VIEWED_PATIENTPROGRAM", message: "VIEWED_PATIENTPROGRAM_MESSAGE"}, - "RUN_REPORT": {eventType: "RUN_REPORT", message: "RUN_REPORT_MESSAGE"} + "RUN_REPORT": {eventType: "RUN_REPORT", message: "RUN_REPORT_MESSAGE"}, + + // IPD Events + "VIEWED_WARD_LEVEL_DASHBOARD": {eventType: "VIEWED_WARD_LEVEL_DASHBOARD", message: "VIEWED_WARD_LEVEL_DASHBOARD_MESSAGE"}, + "CREATE_SCHEDULED_MEDICATION_TASK": {eventType: "CREATE_SCHEDULED_MEDICATION_TASK", message: "CREATE_SCHEDULED_MEDICATION_TASK_MESSAGE"}, + "EDIT_SCHEDULED_MEDICATION_TASK": {eventType: "EDIT_SCHEDULED_MEDICATION_TASK", message: "EDIT_SCHEDULED_MEDICATION_TASK_MESSAGE"}, + "ADMINISTER_MEDICATION_TASK": {eventType: "ADMINISTER_MEDICATION_TASK", message: "ADMINISTER_MEDICATION_TASK_MESSAGE"}, + "STOP_SCHEDULED_MEDICATION_TASK": {eventType: "STOP_SCHEDULED_MEDICATION_TASK", message: "STOP_SCHEDULED_MEDICATION_TASK_MESSAGE"}, + "SKIP_SCHEDULED_MEDICATION_TASK": {eventType: "SKIP_SCHEDULED_MEDICATION_TASK", message: "SKIP_SCHEDULED_MEDICATION_TASK_MESSAGE"}, + "CREATE_EMERGENCY_MEDICATION_TASK": {eventType: "CREATE_EMERGENCY_MEDICATION_TASK", message: "CREATE_EMERGENCY_MEDICATION_TASK_MESSAGE"} }; diff --git a/ui/app/i18n/adt/locale_en.json b/ui/app/i18n/adt/locale_en.json index c517692658..30da5a99f7 100644 --- a/ui/app/i18n/adt/locale_en.json +++ b/ui/app/i18n/adt/locale_en.json @@ -66,5 +66,6 @@ "BEDMANAGEMENT_DISPOSITION_ADMIT_PATIENT": "Admit Patient", "BEDMANAGEMENT_DISPOSITION_DISCHARGE_PATIENT": "Discharge Patient", "BEDMANAGEMENT_DISPOSITION_TRANSFER_PATIENT": "Transfer Patient", - "IPD_WARD_LIST_SEARCH_KEY": "Search..." -} \ No newline at end of file + "IPD_WARD_LIST_SEARCH_KEY": "Search...", + "CARE_VIEW_LABEL": "Change to Care View" +} diff --git a/ui/app/i18n/ipd/locale_en.json b/ui/app/i18n/ipd/locale_en.json index 6fc94116d5..83666b6a71 100644 --- a/ui/app/i18n/ipd/locale_en.json +++ b/ui/app/i18n/ipd/locale_en.json @@ -74,5 +74,6 @@ "TAGS_UPDATED_SUCCESSFULLY_MESSAGE": "Tags Updated Successfully", "DATE_OF_DISCHARGE_AND_REASON_FOR_DISCHARGE_CANNOT_BE_EMPTY_MESSAGE": "Date of Discharge and Reason for discharge cannot be empty.", "IS_SUCCESSFULLY_ASSIGNED_MESSAGE": "is assigned successfully", - "BED": "Bed" + "BED": "Bed", + "CARE_VIEW_LABEL": "Change to Care View" } diff --git a/ui/app/styles/adt/_adt.scss b/ui/app/styles/adt/_adt.scss index b185a95b5d..cb7ec2d39d 100644 --- a/ui/app/styles/adt/_adt.scss +++ b/ui/app/styles/adt/_adt.scss @@ -86,6 +86,7 @@ div[ng-app="adt"] { input { float: left; min-width: 300px; + width: 300px; margin: 10px 10px; padding: 2px 10px; &:focus { diff --git a/ui/test/unit/adt/controllers/careViewController.spec.js b/ui/test/unit/adt/controllers/careViewController.spec.js new file mode 100644 index 0000000000..4e0023aec5 --- /dev/null +++ b/ui/test/unit/adt/controllers/careViewController.spec.js @@ -0,0 +1,85 @@ +'use strict'; + +describe("CareViewController", function () { + var scope, controller, auditLogService, sessionService, $window; + var state = jasmine.createSpyObj('$state', ['go']); + beforeEach(module('bahmni.adt')); + beforeEach(inject(function ($controller, $rootScope,_$window_) { + controller = $controller; + scope = $rootScope.$new(); + $window = _$window_; + })); + auditLogService = jasmine.createSpyObj('auditLogService', ['log']); + sessionService = jasmine.createSpyObj('sessionService', ['destroy']); + auditLogService.log.and.returnValue({ + then: function(callback) { return callback(); } + }); + sessionService.destroy.and.returnValue({ + then: function() { } + }); + let mockProvider = {name: "__test__provider"} + var createController = function () { + + controller('CareViewController', { + $scope: scope, + $rootScope: {currentProvider: mockProvider, quickLogoutComboKey: 'Escape', cookieExpiryTime:30}, + $state: state, + auditLogService: auditLogService, + sessionService: sessionService, + $window: $window + }); + }; + + it('should create host data and host api', function (){ + createController(); + expect(scope.hostData).toEqual({provider: mockProvider}); + expect(scope.hostApi).not.toBeNull(); + }); + + it('should call onHome when hostApi.onHome is called', function () { + createController(); + scope.hostApi.onHome(); + expect(state.go).toHaveBeenCalledWith('home'); + }); + + it('should call auditLogService.log and sessionService.destroy on logout', function () { + createController(); + scope.hostApi.onLogOut(); + expect(auditLogService.log).toHaveBeenCalledWith(undefined, 'USER_LOGOUT_SUCCESS', undefined, 'MODULE_LABEL_LOGOUT_KEY'); + expect(sessionService.destroy).toHaveBeenCalled(); + }); + + it('should call auditLogService.log while handleAuditEvent is triggered', function (){ + createController(); + scope.hostApi.handleAuditEvent(undefined, 'VIEWED_WARD_LEVEL_DASHBOARD', undefined, 'MODULE_LABEL_INPATIENT_KEY'); + expect(auditLogService.log).toHaveBeenCalledWith(undefined, 'VIEWED_WARD_LEVEL_DASHBOARD', undefined, 'MODULE_LABEL_INPATIENT_KEY'); + }); + + it('should call handleLogoutShortcut on keydown event', function (){ + createController(); + spyOn(scope.hostApi, 'onLogOut'); + $window.dispatchEvent(new KeyboardEvent('keydown', {'key': 'Escape', 'metaKey': true, 'ctrlKey': false})); + expect(scope.hostApi.onLogOut).toHaveBeenCalled(); + }); + + it('should call handleLogoutShortcut on keydown event', function (){ + createController(); + spyOn(scope.hostApi, 'onLogOut'); + $window.dispatchEvent(new KeyboardEvent('keydown', {'key': 'Escape', 'metaKey': false, 'ctrlKey': true})); + expect(scope.hostApi.onLogOut).toHaveBeenCalled(); + }); + + it('should call handleLogoutShortcut on keydown event', function (){ + createController(); + spyOn(scope.hostApi, 'onLogOut'); + $window.dispatchEvent(new KeyboardEvent('keydown', {'key': 'Escape', 'metaKey': false, 'ctrlKey': false})); + expect(scope.hostApi.onLogOut).not.toHaveBeenCalled(); + }); + + it('should remove event listener on scope destroy', function () { + spyOn($window, 'removeEventListener'); + createController(); + scope.$destroy(); + expect($window.removeEventListener).toHaveBeenCalledWith('keydown', jasmine.any(Function)); + }); +}); diff --git a/ui/test/unit/bedmanagement/controllers/careViewController.spec.js b/ui/test/unit/bedmanagement/controllers/careViewController.spec.js new file mode 100644 index 0000000000..c2180dfaf9 --- /dev/null +++ b/ui/test/unit/bedmanagement/controllers/careViewController.spec.js @@ -0,0 +1,85 @@ +'use strict'; + +describe("CareViewController", function () { + var scope, controller, auditLogService, sessionService, $window; + var state = jasmine.createSpyObj('$state', ['go']); + beforeEach(module('bahmni.ipd')); + beforeEach(inject(function ($controller, $rootScope,_$window_) { + controller = $controller; + scope = $rootScope.$new(); + $window = _$window_; + })); + auditLogService = jasmine.createSpyObj('auditLogService', ['log']); + sessionService = jasmine.createSpyObj('sessionService', ['destroy']); + auditLogService.log.and.returnValue({ + then: function(callback) { return callback(); } + }); + sessionService.destroy.and.returnValue({ + then: function() { } + }); + let mockProvider = {name: "__test__provider"} + var createController = function () { + + controller('CareViewController', { + $scope: scope, + $rootScope: {currentProvider: mockProvider, quickLogoutComboKey: 'Escape', cookieExpiryTime:30}, + $state: state, + auditLogService: auditLogService, + sessionService: sessionService, + $window: $window + }); + }; + + it('should create host data and host api', function (){ + createController(); + expect(scope.hostData).toEqual({provider: mockProvider}); + expect(scope.hostApi).not.toBeNull(); + }); + + it('should call onHome when hostApi.onHome is called', function () { + createController(); + scope.hostApi.onHome(); + expect(state.go).toHaveBeenCalledWith('home'); + }); + + it('should call auditLogService.log and sessionService.destroy on logout', function () { + createController(); + scope.hostApi.onLogOut(); + expect(auditLogService.log).toHaveBeenCalledWith(undefined, 'USER_LOGOUT_SUCCESS', undefined, 'MODULE_LABEL_LOGOUT_KEY'); + expect(sessionService.destroy).toHaveBeenCalled(); + }); + + it('should call auditLogService.log while handleAuditEvent is triggered', function (){ + createController(); + scope.hostApi.handleAuditEvent(undefined, 'VIEWED_WARD_LEVEL_DASHBOARD', undefined, 'MODULE_LABEL_INPATIENT_KEY'); + expect(auditLogService.log).toHaveBeenCalledWith(undefined, 'VIEWED_WARD_LEVEL_DASHBOARD', undefined, 'MODULE_LABEL_INPATIENT_KEY'); + }); + + it('should call handleLogoutShortcut on keydown event', function (){ + createController(); + spyOn(scope.hostApi, 'onLogOut'); + $window.dispatchEvent(new KeyboardEvent('keydown', {'key': 'Escape', 'metaKey': true, 'ctrlKey': false})); + expect(scope.hostApi.onLogOut).toHaveBeenCalled(); + }); + + it('should call handleLogoutShortcut on keydown event', function (){ + createController(); + spyOn(scope.hostApi, 'onLogOut'); + $window.dispatchEvent(new KeyboardEvent('keydown', {'key': 'Escape', 'metaKey': false, 'ctrlKey': true})); + expect(scope.hostApi.onLogOut).toHaveBeenCalled(); + }); + + it('should call handleLogoutShortcut on keydown event', function (){ + createController(); + spyOn(scope.hostApi, 'onLogOut'); + $window.dispatchEvent(new KeyboardEvent('keydown', {'key': 'Escape', 'metaKey': false, 'ctrlKey': false})); + expect(scope.hostApi.onLogOut).not.toHaveBeenCalled(); + }); + + it('should remove event listener on scope destroy', function () { + spyOn($window, 'removeEventListener'); + createController(); + scope.$destroy(); + expect($window.removeEventListener).toHaveBeenCalledWith('keydown', jasmine.any(Function)); + }); +}); From 7986c95d66188f74070f20c5861623d7de45ffab Mon Sep 17 00:00:00 2001 From: phanindra28 <68492209+phanindra28@users.noreply.github.com> Date: Fri, 31 May 2024 13:22:57 +0530 Subject: [PATCH 13/24] Phani | BAH-3117 | Create React components for OT Notes Save, Edit & Delete Popups using carbon components (#960) * Phani | Include next-ui in OT Moulde * Phani | Create Notes Save & Delete popups * Phani | Add update flow & integrate BE APIs * Phani | Fix bug in delete flow * Phani | Remove unused Angular code * Phani | Move notes save & delete components into components directory * Phani | Update translations * Phani | Add tests for Save & Delete Popups * Phani | Fix uglify * Phani | Fix uglify by refactoring the imports * Phani | Rename folder otNotes in components --- micro-frontends/package.json | 1 + micro-frontends/public/i18n/locale_en.json | 17 +- .../Components/OtNotes/DeletePopup.jsx | 36 ++ .../Components/OtNotes/DeletePopup.spec.jsx | 29 ++ .../next-ui/Components/OtNotes/OtNotes.scss | 22 + .../next-ui/Components/OtNotes/SavePopup.jsx | 145 ++++++ .../Components/OtNotes/SavePopup.spec.jsx | 140 ++++++ .../__snapshots__/DeletePopup.spec.jsx.snap | 97 ++++ .../__snapshots__/SavePopup.spec.jsx.snap | 441 ++++++++++++++++++ .../src/next-ui/Components/OtNotes/utils.js | 59 +++ .../next-ui/Containers/otNotes/OtNotes.jsx | 29 ++ micro-frontends/src/next-ui/constants.js | 1 + micro-frontends/src/next-ui/index.js | 11 + micro-frontends/src/styles/carbon-theme.scss | 6 +- micro-frontends/src/styles/common.scss | 4 - ui/app/i18n/ot/locale_en.json | 15 +- ui/app/ot/controller/otCalendarController.js | 118 ++--- ui/app/ot/index.html | 13 + ui/app/ot/init.js | 2 +- .../ot/services/surgicalAppointmentService.js | 38 -- ui/app/ot/views/notesModal.html | 47 +- .../controller/otCalendarController.spec.js | 139 +----- 22 files changed, 1088 insertions(+), 322 deletions(-) create mode 100644 micro-frontends/src/next-ui/Components/OtNotes/DeletePopup.jsx create mode 100644 micro-frontends/src/next-ui/Components/OtNotes/DeletePopup.spec.jsx create mode 100644 micro-frontends/src/next-ui/Components/OtNotes/OtNotes.scss create mode 100644 micro-frontends/src/next-ui/Components/OtNotes/SavePopup.jsx create mode 100644 micro-frontends/src/next-ui/Components/OtNotes/SavePopup.spec.jsx create mode 100644 micro-frontends/src/next-ui/Components/OtNotes/__snapshots__/DeletePopup.spec.jsx.snap create mode 100644 micro-frontends/src/next-ui/Components/OtNotes/__snapshots__/SavePopup.spec.jsx.snap create mode 100644 micro-frontends/src/next-ui/Components/OtNotes/utils.js create mode 100644 micro-frontends/src/next-ui/Containers/otNotes/OtNotes.jsx diff --git a/micro-frontends/package.json b/micro-frontends/package.json index 38f9914f0d..a39a778daa 100644 --- a/micro-frontends/package.json +++ b/micro-frontends/package.json @@ -52,6 +52,7 @@ }, "scripts": { "build": "webpack --mode production", + "build:dev": "webpack --mode development", "test": "jest", "test:ci": "yarn test --ci --coverage", "lint": "eslint src/**/*.{js,jsx}" diff --git a/micro-frontends/public/i18n/locale_en.json b/micro-frontends/public/i18n/locale_en.json index e4b3408a79..5e3e8ef006 100644 --- a/micro-frontends/public/i18n/locale_en.json +++ b/micro-frontends/public/i18n/locale_en.json @@ -15,5 +15,20 @@ "SEARCH_REACTION": "Search Reaction", "COMMON_REACTIONS": "Common Reactions", "ALLERGEN": "Allergen", - "EDIT_FORM_ERROR_MESSAGE": "Please enter a value in the mandatory fields or correct the value in the highlighted fields to proceed" + "EDIT_FORM_ERROR_MESSAGE": "Please enter a value in the mandatory fields or correct the value in the highlighted fields to proceed", + "EMPTY_NOTES_ERROR": "Note cannot be empty", + "OT_NOTES": "Notes", + "FROM": "From", + "TO": "To", + "FROM_DATE_BEFORE_TO_DATE_ERROR": "From date should be before To date", + "DATE_OUT_OF_RANGE_ERROR": "Please select date within the valid range", + "DELETE_NOTE_TITLE": "Delete Note", + "YES_KEY": "Yes", + "NO_KEY": "No", + "DELETE_NOTE_MESSAGE": "Are you sure you want to delete this note?", + "NOTES_PLACEHOLDER": "Enter a maximum of 150 characters", + "UPDATE_NOTE_TITLE": "Update Note", + "ADD_NOTE_TITLE": "Add Note", + "LOADING": "Loading...", + "UPDATE": "Update" } \ No newline at end of file diff --git a/micro-frontends/src/next-ui/Components/OtNotes/DeletePopup.jsx b/micro-frontends/src/next-ui/Components/OtNotes/DeletePopup.jsx new file mode 100644 index 0000000000..f3eabe0f5a --- /dev/null +++ b/micro-frontends/src/next-ui/Components/OtNotes/DeletePopup.jsx @@ -0,0 +1,36 @@ +import React, { useState } from "react"; +import PropTypes from "prop-types"; +import { Modal } from "carbon-components-react"; +import "../../../styles/carbon-conflict-fixes.scss"; +import "../../../styles/carbon-theme.scss"; +import { deleteOtNote } from "./utils"; +import {FormattedMessage} from "react-intl"; +import "./OtNotes.scss"; +export function DeletePopup(props) { + const { hostData, hostApi} = props; + const [isLoading, setIsLoading] = useState(false); + return } + primaryButtonText={isLoading? : } + secondaryButtonText={} + onRequestClose={hostApi?.onClose} + onSecondarySubmit={hostApi?.onClose} + onRequestSubmit={() => { + setIsLoading(true); + deleteOtNote(hostData?.noteId).then(() => { + setIsLoading(false); + hostApi?.onSuccess(); + }); + }} + > + + +} + +DeletePopup.propTypes = { + hostData: PropTypes.Object, + hostApi: PropTypes.Object, +} diff --git a/micro-frontends/src/next-ui/Components/OtNotes/DeletePopup.spec.jsx b/micro-frontends/src/next-ui/Components/OtNotes/DeletePopup.spec.jsx new file mode 100644 index 0000000000..c052ef5606 --- /dev/null +++ b/micro-frontends/src/next-ui/Components/OtNotes/DeletePopup.spec.jsx @@ -0,0 +1,29 @@ +import React from "react"; +import {render, screen, waitFor} from "@testing-library/react"; +import { DeletePopup } from "./DeletePopup"; +import {I18nProvider} from "../i18n/I18nProvider"; + +const mockDeletePopup = jest.fn(); + +jest.mock('../i18n/utils'); +jest.mock("./utils", () => ({ + deleteOtNote : () => mockDeletePopup(), +})); +describe("DeletePopup", () => { + it("should render", async () => { + const {container, getByText} = render(); + await waitFor(() => { + expect(getByText("Delete Note")).toBeTruthy(); + expect(container).toMatchSnapshot(); + }); + }); + it("should call deleteOtNote on submit", async () => { + const {getByText}= render(); + await waitFor(() => { + expect(getByText("Delete Note")).toBeTruthy(); + }); + mockDeletePopup.mockResolvedValue(() => {}) + screen.getByText("Yes").click(); + expect(mockDeletePopup).toHaveBeenCalled(); + }); +}); \ No newline at end of file diff --git a/micro-frontends/src/next-ui/Components/OtNotes/OtNotes.scss b/micro-frontends/src/next-ui/Components/OtNotes/OtNotes.scss new file mode 100644 index 0000000000..8aae05d39b --- /dev/null +++ b/micro-frontends/src/next-ui/Components/OtNotes/OtNotes.scss @@ -0,0 +1,22 @@ +.ot-notes-popup{ + .bx--modal-close{ + top: 0; + right: 0; + &:hover{ + background-color: #e5e5e5 !important; + } + } + .date-range{ + display: flex; + justify-content: space-between; + } + .bx--date-picker.bx--date-picker--single .bx--date-picker__input{ + width: 230px; + } + .error-text{ + color: #da1e28; + } + .bx--date-picker__input:disabled{ + color: #8d8d8d; + } +} \ No newline at end of file diff --git a/micro-frontends/src/next-ui/Components/OtNotes/SavePopup.jsx b/micro-frontends/src/next-ui/Components/OtNotes/SavePopup.jsx new file mode 100644 index 0000000000..5377b4a30b --- /dev/null +++ b/micro-frontends/src/next-ui/Components/OtNotes/SavePopup.jsx @@ -0,0 +1,145 @@ +import React, {useEffect, useState} from "react"; +import PropTypes from "prop-types"; +import {Modal, TextArea, DatePicker, DatePickerInput} from "carbon-components-react"; +import "../../../styles/carbon-conflict-fixes.scss"; +import "../../../styles/carbon-theme.scss"; +import "./OtNotes.scss"; +import {FormattedMessage, useIntl} from "react-intl"; +import {I18nProvider} from '../i18n/I18nProvider'; +import moment from "moment"; +import {saveNote, updateNoteForADay} from "./utils"; + +export function SavePopup(props) { + const {hostData, hostApi} = props; + const { + notes, + weekEndDateTime = moment().endOf('day'), + weekStartDateTime = moment().startOf('day'), + isDayView, + noteId, + noteDate, + providerUuid + } = hostData; + const [modalNotes, setModalNotes] = useState(notes || ""); + const [startDate, setStartDate] = useState(new Date(noteDate)); + const [endDate, setEndDate] = useState(new Date(noteDate)); + const [shouldShowErrors, setShouldShowErrors] = useState(false); + const validMinStartDate = noteId || isDayView ? new Date(noteDate) : new Date(weekStartDateTime); + const [validMinEndDate, setValidMinEndDate] = useState(noteId || isDayView ? new Date(noteDate) : new Date(weekEndDateTime)); + const [isLoading, setIsLoading] = useState(false); + const intl = useIntl(); + + const hasActiveErrors = () => { + return !modalNotes || !startDate || !endDate || startDate > endDate; + } + + const handleSave = () => { + if (noteId) { + updateNoteForADay(noteId, modalNotes, providerUuid).then(() => { + setIsLoading(false); + hostApi?.onSuccess(); + }); + } else if (isDayView) { + saveNote(modalNotes, startDate).then(() => { + setIsLoading(false); + hostApi?.onSuccess(); + }); + } else { + saveNote(modalNotes, startDate, endDate).then(() => { + setIsLoading(false); + hostApi?.onSuccess(); + }); + } + } + useEffect(() => { + setValidMinEndDate(startDate); + }, [startDate]); + return ( + + : + } + primaryButtonText={isLoading ? + : noteId ? + : + } + secondaryButtonText={} + onRequestClose={hostApi?.onClose} + onRequestSubmit={() => { + setShouldShowErrors(true); + if (!hasActiveErrors()) { + setIsLoading(true); + handleSave(); + } + }} + > + +
+
+
+
+
+
+
+
+ +
+ + +
+
+
+
+
+
+
+
+
+
+ +
+ + +
+
+
+
+
+
+
+
+ +
+ + Focus sentinel + +
+
+`; diff --git a/micro-frontends/src/next-ui/Components/OtNotes/utils.js b/micro-frontends/src/next-ui/Components/OtNotes/utils.js new file mode 100644 index 0000000000..5382e2845d --- /dev/null +++ b/micro-frontends/src/next-ui/Components/OtNotes/utils.js @@ -0,0 +1,59 @@ +import axios from 'axios'; +import { OT_NOTES_BASE_URL } from "../../constants"; + +export const deleteOtNote = async (id) => { + try { + const response = await axios.delete(OT_NOTES_BASE_URL + "/" + id, { + headers: {"Accept": "application/json", "Content-Type": "application/json", withCredentials: true} + }); + return response.data; + + } catch (e) { + console.error(e); + } +} + +export const constructPayload = function (noteDate, note, noteEndDate) { + const payload = []; + const currentDate = new Date(noteDate); + while (currentDate <= noteEndDate) { + payload.push({ + noteTypeName: "OT module", + noteDate: new Date(currentDate), + noteText: note + }); + currentDate.setDate(currentDate.getDate() + 1); + } + return payload; +}; + +export const saveNote = async (note, startDate, endDate) => { + try{ + const payload = endDate != null ? constructPayload(startDate, note, endDate) : [{ + noteTypeName: "OT module", + noteDate: startDate, + noteText: note + }]; + const response = await axios.post(OT_NOTES_BASE_URL, payload); + return response.data; + } + catch (e) { + console.error(e); + } +} + +export const updateNoteForADay = async (noteId, note, providerUuid) => { + const payload = { + noteText: note, + providerUuid: providerUuid + }; + try{ + const response = await axios.post(OT_NOTES_BASE_URL + "/" + noteId, payload, { + headers: {"Accept": "application/json", "Content-Type": "application/json"} + }); + return response.data; + } + catch (e) { + console.error(e); + } +}; \ No newline at end of file diff --git a/micro-frontends/src/next-ui/Containers/otNotes/OtNotes.jsx b/micro-frontends/src/next-ui/Containers/otNotes/OtNotes.jsx new file mode 100644 index 0000000000..e0b8ea9717 --- /dev/null +++ b/micro-frontends/src/next-ui/Containers/otNotes/OtNotes.jsx @@ -0,0 +1,29 @@ +import React from "react"; +import PropTypes from "prop-types"; +import {I18nProvider} from "../../Components/i18n/I18nProvider"; +import {SavePopup} from "../../Components/OtNotes/SavePopup"; +import {DeletePopup} from "../../Components/OtNotes/DeletePopup"; + +export function OtNotesDeletePopup(props) { + return ( + + + + ); +} +export function OtNotesSavePopup(props) { + return ( + + + + ); +} +OtNotesDeletePopup.propTypes = { + hostData: PropTypes.object, + hostApi: PropTypes.object, +} + +OtNotesSavePopup.propTypes = { + hostData: PropTypes.object, + hostApi: PropTypes.object, +} \ No newline at end of file diff --git a/micro-frontends/src/next-ui/constants.js b/micro-frontends/src/next-ui/constants.js index 7dbc03ceb2..cf4cf88f09 100644 --- a/micro-frontends/src/next-ui/constants.js +++ b/micro-frontends/src/next-ui/constants.js @@ -32,4 +32,5 @@ export const GET_DRUG_ACKNOWLEDGEMENT_URL = RESTWS_V1 + "/bahmnicore/sql?q={prop export const EMERGENCY_MEDICATIONS_BASE_URL = RESTWS_V1 + "/ipd/adhocMedicationAdministrations/{medication_administration_uuid}"; export const GET_PROVIDER_UUID_URL = RESTWS_V1 + "/session"; export const SAVE_ALLERGIES_URL = RESTWS_V1 + "/patient/{patientId}/allergy"; +export const OT_NOTES_BASE_URL = RESTWS_V1 + "/notes"; diff --git a/micro-frontends/src/next-ui/index.js b/micro-frontends/src/next-ui/index.js index 6a8956d486..b523398d05 100644 --- a/micro-frontends/src/next-ui/index.js +++ b/micro-frontends/src/next-ui/index.js @@ -1,6 +1,7 @@ import { React2AngularBridgeBuilder } from "../utils/bridge-builder"; import { PatientAlergiesControl } from "./Containers/patientAlergies/PatientAlergiesControl"; import { FormDisplayControl } from "./Containers/formDisplayControl/FormDisplayControl"; +import { OtNotesSavePopup, OtNotesDeletePopup } from "./Containers/otNotes/OtNotes"; const MODULE_NAME = "bahmni.mfe.nextUi"; @@ -20,3 +21,13 @@ builder.createComponentWithTranslationForwarding( "FormDisplayControl", FormDisplayControl ); + +builder.createComponentWithTranslationForwarding( + "OtNotesSavePopup", + OtNotesSavePopup +) + +builder.createComponentWithTranslationForwarding( + "OtNotesDeletePopup", + OtNotesDeletePopup +) \ No newline at end of file diff --git a/micro-frontends/src/styles/carbon-theme.scss b/micro-frontends/src/styles/carbon-theme.scss index 94e7459f7e..682a548996 100644 --- a/micro-frontends/src/styles/carbon-theme.scss +++ b/micro-frontends/src/styles/carbon-theme.scss @@ -7,7 +7,11 @@ $css--default-type: false; $css--reset: false; $css--body: false; @import "~carbon-components/scss/globals/scss/styles.scss"; -[class*="bx--"]{ +.next-ui{ + color: #393939; + font-family: 'IBM Plex Sans', sans-serif; +} +.next-ui > [class*="bx--"]{ &:disabled{ -webkit-text-fill-color: #8d8d8d; opacity: 1; diff --git a/micro-frontends/src/styles/common.scss b/micro-frontends/src/styles/common.scss index 49d47ea606..90a74209e0 100644 --- a/micro-frontends/src/styles/common.scss +++ b/micro-frontends/src/styles/common.scss @@ -19,10 +19,6 @@ height: 64px; } } -.next-ui{ - color: #393939; - font-family: 'IBM Plex Sans', sans-serif; -} .section-next-ui{ background-color: #fff; diff --git a/ui/app/i18n/ot/locale_en.json b/ui/app/i18n/ot/locale_en.json index 5d84a73887..b85a1ea2b2 100644 --- a/ui/app/i18n/ot/locale_en.json +++ b/ui/app/i18n/ot/locale_en.json @@ -14,8 +14,6 @@ "DATE": "Date", "DISCARD": "Discard", "SAVE": "Save", - "UPDATE": "Update", - "ADD_SURGICAL_APPOINTMENT": "Add Surgery", "EDIT_SURGICAL_APPOINTMENT": "Edit Surgery", "MESSAGE_DIALOG_OPTION_COPY" : "Copy Error", @@ -115,16 +113,5 @@ "ACTUAL_TIME_ADDED_TO_KEY":"Actual time added to ", "OT_SURGICAL_BLOCK_ACROSS_MULTIPLE_DAYS_CONFIRMATION_MESSAGE": "Are you sure you want to create a surgical block across days?", "MOVING_KEY": "Moving", - "FROM_KEY": "from", - "EMPTY_NOTES_ERROR": "Note cannot be empty", - "NOTE_TITLE": "Note", - "FROM": "From", - "TO": "To", - "FROM_DATE_BEFORE_TO_DATE_ERROR": "From date should be before To date", - "DATE_OUT_OF_RANGE_ERROR": "Please select date within the valid range", - "DELETE_NOTE_TITLE": "Delete Note", - "YES_KEY": "Yes", - "NO_KEY": "No", - "DELETE_NOTE_MESSAGE": "Are you sure you want to delete this note?", - "NOTES_PLACEHOLDER": "Enter a maximum of 150 characters" + "FROM_KEY": "from" } diff --git a/ui/app/ot/controller/otCalendarController.js b/ui/app/ot/controller/otCalendarController.js index 2b76170a4f..fba8e9bee5 100644 --- a/ui/app/ot/controller/otCalendarController.js +++ b/ui/app/ot/controller/otCalendarController.js @@ -47,7 +47,8 @@ angular.module('bahmni.ot') $scope.showNotesPopup = function (weekStartDate, addIndex) { const currentDate = new Date(weekStartDate); - if (addIndex === undefined) { + const isDayView = addIndex === undefined; + if (isDayView) { addIndex = 0; } currentDate.setDate(currentDate.getDate() + addIndex); @@ -59,101 +60,55 @@ angular.module('bahmni.ot') setValidStartDate(currentDate); setValidEndDate(currentDate); } + $scope.hostData = { + notes: '', + noteId: '', + isDayView: isDayView, + weekStartDateTime: $scope.validStartDate, + weekEndDateTime: $scope.validEndDate, + noteDate: currentDate + }; }; $scope.showNotesPopupEdit = function (weekStartDate, addIndex) { - const note = $scope.getNotesForDay(weekStartDate, addIndex); - const currentDate = new Date(weekStartDate); - $scope.otNotesField = note; - currentDate.setDate(currentDate.getDate() + addIndex); - $scope.notesStartDate = currentDate; - $scope.notesEndDate = currentDate; $scope.isModalVisible = true; - $scope.isEdit = true; - $scope.dateOutOfRangeError = false; - }; - $scope.closeNotes = function () { - $scope.isModalVisible = false; - $scope.startDateBeforeEndDateError = false; - $scope.emptyNoteError = false; - $scope.dateOutOfRangeError = false; - $scope.notesStartDate = undefined; - $scope.notesEndDate = undefined; - $scope.otNotesField = ''; - $scope.isEdit = false; - }; - $scope.setNotesEndDate = function (date) { - $scope.dateOutOfRangeError = date === undefined; - $scope.startDateBeforeEndDateError = date < $scope.notesStartDate; - $scope.notesEndDate = date; + const getNoteForTheDay = $scope.getNotesForWeek(weekStartDate, addIndex); + $scope.hostData = { + notes: getNoteForTheDay[0].noteText, + noteId: getNoteForTheDay[0].noteId, + isDayView: $state.weekOrDay === 'day', + weekStartDateTime: $scope.validStartDate, + weekEndDateTime: $scope.validEndDate, + noteDate: new Date(getNoteForTheDay[0].noteDate), + providerUuid: $rootScope.currentProvider.uuid + }; }; - $scope.setNotesStartDate = function (date) { - $scope.dateOutOfRangeError = date === undefined; - $scope.startDateBeforeEndDateError = date > $scope.notesEndDate; - $scope.notesStartDate = date; - }; - - $scope.setNotes = function (notes) { - $scope.otNotesField = notes; - if (notes) { - $scope.emptyNoteError = false; - } - if (notes && notes.id) { - $scope.notesId = notes.id; - } - }; $scope.openDeletePopup = function (weekStartDate, index) { if (weekStartDate) { $scope.currentDate = new Date(weekStartDate); $scope.currentDate.setDate($scope.currentDate.getDate() + index); - } - $scope.showDeletePopUp = true; - }; - $scope.closeDeletePopup = function () { - $scope.showDeletePopUp = false; - }; - $scope.deleteNotes = function () { - var noteId; - if ($scope.weekOrDay === "week") { - noteId = $scope.notesForWeek[$scope.currentDate].noteId; - } - surgicalAppointmentService.deleteNoteForADay(noteId || $scope.noteId); - $scope.showDeletePopUp = false; - $state.go("otScheduling", {viewDate: $scope.viewDate}, {reload: true}); - }; - - $scope.saveNotes = function () { - if ($scope.startDateBeforeEndDateError || $scope.dateOutOfRangeError) { - return; - } - if (!$scope.otNotesField) { - $scope.emptyNoteError = true; - return; - } - if ($scope.isDayView) { - surgicalAppointmentService.saveNoteForADay($scope.viewDate, $scope.otNotesField); + $scope.hostData = { + noteId: $scope.getNotesForWeek(weekStartDate, index)[0].noteId + }; } else { - surgicalAppointmentService.saveNoteForADay($scope.notesStartDate, $scope.otNotesField, $scope.notesEndDate); + $scope.hostData = { + noteId: $scope.noteId + }; } - $state.go("otScheduling", {viewDate: $scope.viewDate}, {reload: true}); + $scope.showDeletePopUp = true; }; - $scope.updateNotes = function () { - if ($scope.startDateBeforeEndDateError || $scope.dateOutOfRangeError) { - return; - } - if (!$scope.otNotesField) { - $scope.emptyNoteError = true; - return; - } - var note; - if ($scope.weekOrDay === "week") { - note = $scope.notesForWeek[$scope.notesStartDate]; + $scope.hostApi = { + onSuccess: function () { + $state.go("otScheduling", {viewDate: $scope.viewDate}, {reload: true}); + }, + onClose: function () { + $scope.$apply(function () { + $scope.showDeletePopUp = false; + $scope.isModalVisible = false; + }); } - surgicalAppointmentService.updateNoteForADay(note ? note.noteId : $scope.noteId, $scope.otNotesField, $rootScope.currentProvider.uuid); - $state.go("otScheduling", {viewDate: $scope.viewDate}, {reload: true}); }; - var heightPerMin = 120 / $scope.dayViewSplit; var showToolTipForNotes = function () { $('.notes-text').tooltip({ @@ -179,7 +134,6 @@ angular.module('bahmni.ot') $scope.editDisabled = true; $scope.cancelDisabled = true; $scope.addActualTimeDisabled = true; - $scope.otNotes = ""; $scope.isModalVisible = false; $scope.showDeletePopUp = false; $scope.dayViewSplit = parseInt($scope.dayViewSplit) > 0 ? parseInt($scope.dayViewSplit) : 60; diff --git a/ui/app/ot/index.html b/ui/app/ot/index.html index 5f7477ddbf..3e8a671805 100644 --- a/ui/app/ot/index.html +++ b/ui/app/ot/index.html @@ -13,6 +13,8 @@ + + @@ -54,6 +56,17 @@ + + + + + + + + + + + diff --git a/ui/app/ot/init.js b/ui/app/ot/init.js index 9d278f4361..e37188bcf4 100644 --- a/ui/app/ot/init.js +++ b/ui/app/ot/init.js @@ -3,5 +3,5 @@ var Bahmni = Bahmni || {}; Bahmni.OT = Bahmni.OT || {}; -angular.module('bahmni.ot', ['bahmni.common.conceptSet', 'bahmni.common.logging']); +angular.module('bahmni.ot', ['bahmni.common.conceptSet', 'bahmni.common.logging', 'bahmni.mfe.nextUi']); diff --git a/ui/app/ot/services/surgicalAppointmentService.js b/ui/app/ot/services/surgicalAppointmentService.js index 0c4997df33..38ebba22ae 100644 --- a/ui/app/ot/services/surgicalAppointmentService.js +++ b/ui/app/ot/services/surgicalAppointmentService.js @@ -92,42 +92,4 @@ angular.module('bahmni.ot') withCredentials: true }); }; - - var constructPayload = function (noteDate, note, noteEndDate) { - const payload = []; - const currentDate = new Date(noteDate); - // eslint-disable-next-line no-unmodified-loop-condition - while (currentDate <= noteEndDate) { - payload.push({ - noteTypeName: "OT module", - noteDate: new Date(currentDate), - noteText: note - }); - currentDate.setDate(currentDate.getDate() + 1); - } - return payload; - }; - - this.saveNoteForADay = function (noteDate, note, noteEndDate) { - const payload = noteEndDate != null ? constructPayload(noteDate, note, noteEndDate) : [{ - noteTypeName: "OT module", - noteDate: noteDate, - noteText: note - }]; - const headers = {"Accept": "application/json", "Content-Type": "application/json"}; - return $http.post(Bahmni.OT.Constants.notesUrl, payload, headers); - }; - - this.updateNoteForADay = function (noteId, note, providerUuid) { - const payload = { - noteText: note, - providerUuid: providerUuid - }; - const headers = {"Accept": "application/json", "Content-Type": "application/json"}; - return $http.post(Bahmni.OT.Constants.notesUrl + "/" + noteId, payload, headers); - }; - this.deleteNoteForADay = function (noteId) { - const headers = {"Accept": "application/json", "Content-Type": "application/json", withCredentials: true}; - return $http.delete(Bahmni.OT.Constants.notesUrl + "/" + noteId, headers); - }; }]); diff --git a/ui/app/ot/views/notesModal.html b/ui/app/ot/views/notesModal.html index 21f9ec5d2e..b97aca363e 100644 --- a/ui/app/ot/views/notesModal.html +++ b/ui/app/ot/views/notesModal.html @@ -1,45 +1,6 @@ -
-
- × -
-
{{'NOTE_TITLE' | translate}}
- -
-
{{'EMPTY_NOTES_ERROR' | translate }}
-
-
-
{{'FROM' | translate}}
- -
-
-
{{'TO' | translate}}
- -
-
- {{'FROM_DATE_BEFORE_TO_DATE_ERROR' | translate}} -
-
- {{'DATE_OUT_OF_RANGE_ERROR' | translate}} -
-
- - - -
+
+
- -
-
- × -
-
{{'DELETE_NOTE_TITLE' | translate}}
-

{{'DELETE_NOTE_MESSAGE' | translate}}

-
- - -
+
+
diff --git a/ui/test/unit/ot/controller/otCalendarController.spec.js b/ui/test/unit/ot/controller/otCalendarController.spec.js index 77bb4ff024..525ff7b1d1 100644 --- a/ui/test/unit/ot/controller/otCalendarController.spec.js +++ b/ui/test/unit/ot/controller/otCalendarController.spec.js @@ -5,7 +5,7 @@ describe("otCalendarController", function () { var state = jasmine.createSpyObj('$state', ['go']); var locationService = jasmine.createSpyObj('locationService', ['getAllByTag']); spinner = jasmine.createSpyObj('spinner', ['forPromise', 'then', 'catch']); - var surgicalAppointmentService = jasmine.createSpyObj('surgicalAppointmentService', ['getSurgicalBlocksInDateRange', 'getSurgeons', 'getBulkNotes', 'saveNoteForADay']); + var surgicalAppointmentService = jasmine.createSpyObj('surgicalAppointmentService', ['getSurgicalBlocksInDateRange', 'getSurgeons', 'getBulkNotes']); var ngDialog = jasmine.createSpyObj('ngDialog', ['open']); var appService = jasmine.createSpyObj('appService', ['getAppDescriptor']); var appDescriptor = jasmine.createSpyObj('appDescriptor', ['getConfigValue']); @@ -287,141 +287,4 @@ describe("otCalendarController", function () { expect(scope.blockedOtsOfTheDay).toEqual(["uuid1", "uuid2"]); }); }); - - describe('notes', function () { - it('should set startDate and endDate to selectedDate when showNotesPopup is called', function () { - surgicalAppointmentService.getSurgicalBlocksInDateRange.and.callFake(function () { - return {data: {results: weekSurgicalBlocks}}; - }); - scope.weekOrDay = 'week'; - scope.weekStartDate = moment('2020-04-06').toDate(); - surgicalAppointmentService.getSurgeons.and.callFake(function () { - return {data: {results: surgeons}}; - }); - surgicalAppointmentService.getBulkNotes.and.callFake(function () { - return {data: notes} - }); - createController(); - const weekStartDate = moment('2023-07-02').toDate(); - const expectedDate = moment('2023-07-05').toDate(); - scope.showNotesPopup(weekStartDate, 3); - expect(scope.notesStartDate).toEqual(expectedDate); - expect(scope.notesEndDate).toEqual(expectedDate); - }); - it('should set startDate and endDate to selectedDate when we try to edit existing note', function () { - surgicalAppointmentService.getSurgicalBlocksInDateRange.and.callFake(function () { - return {data: {results: weekSurgicalBlocks}}; - }); - scope.weekOrDay = 'week'; - scope.weekStartDate = moment('2020-04-06').toDate(); - surgicalAppointmentService.getSurgeons.and.callFake(function () { - return {data: {results: surgeons}}; - }); - surgicalAppointmentService.getBulkNotes.and.callFake(function () { - return {data: notes} - }); - createController(); - const weekStartDate = moment('2023-07-02').toDate(); - const expectedDate = moment('2023-07-05').toDate(); - scope.showNotesPopupEdit(weekStartDate, 3, 'note to show'); - expect(scope.notesStartDate).toEqual(expectedDate); - expect(scope.notesEndDate).toEqual(expectedDate); - }); - it('should clear startDate and endDate notes popup is closed', function () { - surgicalAppointmentService.getSurgicalBlocksInDateRange.and.callFake(function () { - return {data: {results: weekSurgicalBlocks}}; - }); - scope.weekOrDay = 'week'; - scope.weekStartDate = moment('2020-04-06').toDate(); - surgicalAppointmentService.getSurgeons.and.callFake(function () { - return {data: {results: surgeons}}; - }); - surgicalAppointmentService.getBulkNotes.and.callFake(function () { - return {data: notes} - }); - createController(); - const weekStartDate = moment('2023-07-02').toDate(); - const expectedDate = moment('2023-07-05').toDate(); - scope.showNotesPopupEdit(weekStartDate, 3, 'note to show'); - expect(scope.notesStartDate).toEqual(expectedDate); - expect(scope.notesEndDate).toEqual(expectedDate); - scope.closeNotes(); - expect(scope.notesStartDate).toEqual(undefined); - expect(scope.notesEndDate).toEqual(undefined); - expect(scope.otNotesField).toEqual(''); - }); - it('should set the notes popup fields and save', function () { - surgicalAppointmentService.getSurgicalBlocksInDateRange.and.callFake(function () { - return {data: {results: weekSurgicalBlocks}}; - }); - scope.weekOrDay = 'week'; - scope.weekStartDate = moment('2020-04-06').toDate(); - surgicalAppointmentService.getSurgeons.and.callFake(function () { - return {data: {results: surgeons}}; - }); - surgicalAppointmentService.getBulkNotes.and.callFake(function () { - return {data: notes} - }); - createController(); - const viewDate = moment('2023-07-05').toDate(); - scope.setNotesStartDate(viewDate); - scope.setNotesEndDate(viewDate); - scope.setNotes('note to show'); - scope.saveNotes(); - expect(scope.notesStartDate).toEqual(viewDate); - expect(scope.notesEndDate).toEqual(viewDate); - expect(scope.otNotesField).toEqual('note to show'); - }); - it('should set error field for empty notes when we try to save empty notes', function () { - surgicalAppointmentService.getSurgicalBlocksInDateRange.and.callFake(function () { - return {data: {results: weekSurgicalBlocks}}; - }); - scope.weekOrDay = 'week'; - scope.weekStartDate = moment('2020-04-06').toDate(); - surgicalAppointmentService.getSurgeons.and.callFake(function () { - return {data: {results: surgeons}}; - }); - surgicalAppointmentService.getBulkNotes.and.callFake(function () { - return {data: notes} - }); - createController(); - scope.setNotes(''); - scope.saveNotes(); - expect(scope.emptyNoteError).toEqual(true); - }); - it('should set error field when start Date is before endDate', function () { - surgicalAppointmentService.getSurgicalBlocksInDateRange.and.callFake(function () { - return {data: {results: weekSurgicalBlocks}}; - }); - scope.weekOrDay = 'week'; - scope.weekStartDate = moment('2020-04-06').toDate(); - surgicalAppointmentService.getSurgeons.and.callFake(function () { - return {data: {results: surgeons}}; - }); - surgicalAppointmentService.getBulkNotes.and.callFake(function () { - return {data: notes} - }); - createController(); - scope.setNotesStartDate(moment('2023-07-05').toDate()); - scope.setNotesEndDate(moment('2023-07-02').toDate()); - scope.saveNotes(); - expect(scope.startDateBeforeEndDateError).toEqual(true); - }); - it('should get styling of last block', function () { - surgicalAppointmentService.getSurgicalBlocksInDateRange.and.callFake(function () { - return {data: {results: weekSurgicalBlocks}}; - }); - scope.weekOrDay = 'week'; - scope.weekStartDate = moment('2020-04-06').toDate(); - surgicalAppointmentService.getSurgeons.and.callFake(function () { - return {data: {results: surgeons}}; - }); - surgicalAppointmentService.getBulkNotes.and.callFake(function () { - return {data: notes} - }); - createController(); - const style = scope.styleForBlock(6); - expect(style).toEqual({ 'border-right': '.5px solid lightgrey'}); - }); - }); }); From 8d8c8c47cba959dd3381ca59d9fb34b2fffa12b4 Mon Sep 17 00:00:00 2001 From: Soorya Kumaran C <90232857+SooryaKumaranC-tw@users.noreply.github.com> Date: Fri, 31 May 2024 17:12:18 +0530 Subject: [PATCH 14/24] Soorya | BAH-3056 | Set dosing rule dropdown unselected for edit (#961) * Soorya | BAH-3056 | Set dosing rule dropdown unselected for edit * Fix a test case for the dosing rule edit issue --- .../consultation/controllers/addTreatmentController.js | 1 + .../unit/clinical/controllers/addTreatmentController.spec.js | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/ui/app/clinical/consultation/controllers/addTreatmentController.js b/ui/app/clinical/consultation/controllers/addTreatmentController.js index 7d9e68e409..73f504decc 100644 --- a/ui/app/clinical/consultation/controllers/addTreatmentController.js +++ b/ui/app/clinical/consultation/controllers/addTreatmentController.js @@ -366,6 +366,7 @@ angular.module('bahmni.clinical') if (treatment.dosingRule != null || treatment.dosingRule != undefined) { var visitUuid = treatmentConfig.orderSet.calculateDoseOnlyOnCurrentVisitValues ? $scope.activeVisit.uuid : undefined; var calculatedDose = orderSetService.getCalculatedDose($scope.patient.uuid, treatment.drug.name, treatment.uniformDosingType.dose, treatment.uniformDosingType.doseUnits, '', treatment.dosingRule, visitUuid); + treatment.dosingRule = undefined; calculatedDose.then(function (calculatedDosage) { treatment.uniformDosingType.dose = calculatedDosage.dose; treatment.calculateQuantityAndUnit(); diff --git a/ui/test/unit/clinical/controllers/addTreatmentController.spec.js b/ui/test/unit/clinical/controllers/addTreatmentController.spec.js index 8376101281..5cf1f80344 100644 --- a/ui/test/unit/clinical/controllers/addTreatmentController.spec.js +++ b/ui/test/unit/clinical/controllers/addTreatmentController.spec.js @@ -2148,7 +2148,8 @@ describe("AddTreatmentController", function () { describe('calculateDose', function() { it('should call getCalculatedDose with correct parameters', function() { - var treatment = { dosingRule: 'someRule', drug: { name: 'Drug A' }, uniformDosingType: { dose: 10, doseUnits: 'mg' }, calculateQuantityAndUnit}; + var dosingRule = 'someRule'; + var treatment = { dosingRule: dosingRule, drug: { name: 'Drug A' }, uniformDosingType: { dose: 10, doseUnits: 'mg' }, calculateQuantityAndUnit}; scope.calculateDose(treatment); expect(orderSetService.getCalculatedDose).toHaveBeenCalledWith( @@ -2157,7 +2158,7 @@ describe("AddTreatmentController", function () { treatment.uniformDosingType.dose, treatment.uniformDosingType.doseUnits, '', - treatment.dosingRule, + dosingRule, undefined ); }); From c5e57dc38f5f106c1f39288677bd188559429dec Mon Sep 17 00:00:00 2001 From: bahmni-infra Date: Sat, 1 Jun 2024 00:19:44 +0000 Subject: [PATCH 15/24] [Bahmni Infra] | Add. Update Translation Resources --- ui/app/i18n/adt/locale_en.json | 2 +- ui/app/i18n/clinical/locale_en.json | 3 +-- ui/app/i18n/registration/locale_en.json | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/ui/app/i18n/adt/locale_en.json b/ui/app/i18n/adt/locale_en.json index 30da5a99f7..1f06e27682 100644 --- a/ui/app/i18n/adt/locale_en.json +++ b/ui/app/i18n/adt/locale_en.json @@ -68,4 +68,4 @@ "BEDMANAGEMENT_DISPOSITION_TRANSFER_PATIENT": "Transfer Patient", "IPD_WARD_LIST_SEARCH_KEY": "Search...", "CARE_VIEW_LABEL": "Change to Care View" -} +} \ No newline at end of file diff --git a/ui/app/i18n/clinical/locale_en.json b/ui/app/i18n/clinical/locale_en.json index 3ccd5f208a..0c477ba222 100644 --- a/ui/app/i18n/clinical/locale_en.json +++ b/ui/app/i18n/clinical/locale_en.json @@ -168,7 +168,6 @@ "ENTER_PATIENT_WEIGHT_ERROR": "Patient weight has not been captured recently. Prescription cannot be created.", "ENTER_DIAGNOSIS_ERROR": "Primary diagnosis has not been captured. Prescription cannot be created", "PATIENT_WEIGHT_AND_DIAGNOSIS_ERROR": "Patient weight has not been captured recently & no primary diagnosis is found. Prescription cannot be created", - "DRUG_DETAILS_DRUG_NAME": "Drug", "DRUG_DETAILS_DOSE_INFO": "Dose", "DRUG_DETAILS_ROUTE": "Route", @@ -389,4 +388,4 @@ "CDSS_ALERT_SAVE_ERROR": "A CDSS Critical Alert needs to be addressed in Medications Tab before continuing with Save operation", "CDSS_ACTION": "Please go to Medications Tab to take further action", "MEDICATION_PRINT": "Print" -} +} \ No newline at end of file diff --git a/ui/app/i18n/registration/locale_en.json b/ui/app/i18n/registration/locale_en.json index 20688dc432..97385aaa6e 100644 --- a/ui/app/i18n/registration/locale_en.json +++ b/ui/app/i18n/registration/locale_en.json @@ -124,4 +124,4 @@ "PATIENT_ATTRIBUTE_MIDDLE_NAME_LOCAL": "Middle Name in Arabic", "RELATED_PATIENT_IDENTIFIER_LABEL": "Patient", "REGISTRATION_LABEL_SAVE_REDIRECTION": "Saved and Visit Started Successfully" -} +} \ No newline at end of file From bc64064d9b6944ecb4acd4526f4f650b8840e066 Mon Sep 17 00:00:00 2001 From: Parvathy Babu <102500787+parvathy00@users.noreply.github.com> Date: Tue, 4 Jun 2024 12:56:39 +0530 Subject: [PATCH 16/24] BAH-3923 | Fix. Upgrade Ruby Version --- .github/workflows/build_publish.yml | 4 ++-- .github/workflows/validate_pr.yml | 6 +++--- README.md | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build_publish.yml b/.github/workflows/build_publish.yml index 83da36aa0b..067186bc53 100644 --- a/.github/workflows/build_publish.yml +++ b/.github/workflows/build_publish.yml @@ -31,10 +31,10 @@ jobs: uses: actions/setup-node@v1 with: node-version: 14.x - - name: Use Ruby 2.5 + - name: Use Ruby 3.1 uses: ruby/setup-ruby@v1 with: - ruby-version: 2.5 + ruby-version: 3.1 - run: npm install -g bower - run: npm install -g grunt-cli - run: gem install compass diff --git a/.github/workflows/validate_pr.yml b/.github/workflows/validate_pr.yml index ec48f095b4..176864d8f1 100644 --- a/.github/workflows/validate_pr.yml +++ b/.github/workflows/validate_pr.yml @@ -1,7 +1,7 @@ name: Validate PR on: - pull_request: + pull_request: branches: [ master ] jobs: @@ -13,10 +13,10 @@ jobs: uses: actions/setup-node@v1 with: node-version: 14.x - - name: Use Ruby 2.5 + - name: Use Ruby 3.1 uses: ruby/setup-ruby@v1 with: - ruby-version: 2.5 + ruby-version: 3.1 - run: npm install -g bower - run: npm install -g grunt-cli - run: gem install compass diff --git a/README.md b/README.md index d27d1748ee..88d30e4481 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ These steps need to performed ONLY the FIRST TIME you set up this code. 4. Install Compass: - Compass compiles SASS/SCSS into CSS. - Requires ruby (It's recommended to install ruby also using rvm. See install [rvm with ruby](https://stackify.com/rvm-how-to-get-started-and-manage-your-ruby-installations/)). - - Ruby version: 2.6.6 + - Ruby version: 3.1 - Once ruby is installed, you can install compass using: `gem install compass` ### Build commands From ada38560fc767b908d2576db8ac29f5d43cf9e7a Mon Sep 17 00:00:00 2001 From: Himabindu T Date: Wed, 5 Jun 2024 18:47:15 +0530 Subject: [PATCH 17/24] BAH-3788 | Ability to configure Forms Display control in the Program Enrolment Page (#955) * Ayush | BAH-3788 | feat: added observation forms widget in patient program page [Ayush] * Ayush |test: added test and feedback * Ayush | refactor: refactored some code and fixed test cases * Ayush | fix: minified conversion issue [Ayush] * Parvathy | BAH-3788 | Fix. Export findByEncounterUuid Function * Parvathy | BAH-3788 | Fix. Remove Unwanted Test * Bindu | BAH-3788 | keep the feature behind toggle * Parvathy | BAH-3788 | Refactor. Remove Config Related to Editing of Forms --------- Co-authored-by: parvathy00 --- .../src/next-ui/utils/FormDisplayControl/FormView.js | 7 +------ .../utils/FormDisplayControl/FormView.spec.js | 8 -------- micro-frontends/src/next-ui/utils/utils.js | 2 +- ui/app/common/domain/services/programService.js | 7 ++++++- .../controllers/manageProgramController.js | 12 ++++++++++++ .../programmanagement/views/programEnrollment.html | 9 ++++++++- ui/app/styles/clinical/_program.scss | 8 ++++++++ .../common/domain/services/programService.spec.js | 4 ++++ .../controllers/manageProgramController.spec.js | 2 +- 9 files changed, 41 insertions(+), 18 deletions(-) diff --git a/micro-frontends/src/next-ui/utils/FormDisplayControl/FormView.js b/micro-frontends/src/next-ui/utils/FormDisplayControl/FormView.js index 2a92820560..3ff25b7f1a 100644 --- a/micro-frontends/src/next-ui/utils/FormDisplayControl/FormView.js +++ b/micro-frontends/src/next-ui/utils/FormDisplayControl/FormView.js @@ -1,7 +1,7 @@ import axios from "axios"; -import moment from "moment"; import { ENCOUNTER_BASE_URL } from "../../constants"; import { build } from "../FormDisplayControl/BuildFormView"; +import { formatDate } from "../../utils/utils"; export const findByEncounterUuid = async (encounterUuid) => { const apiURL = ENCOUNTER_BASE_URL.replace("{encounterUuid}", encounterUuid); @@ -66,11 +66,6 @@ export const memberTypes = { COMPLEX: "Complex", BOOLEAN: "Boolean", }; - -export const formatDate = (value, format = "DD-MMM-YYYY") => { - return value ? moment(value).format(format) : value; -}; - export const getValue = (member) => { const { value = "", type, complexData = {}, valueAsString } = member; let finalValue = value?.shortName || value; diff --git a/micro-frontends/src/next-ui/utils/FormDisplayControl/FormView.spec.js b/micro-frontends/src/next-ui/utils/FormDisplayControl/FormView.spec.js index 9909b9da00..9fe14a3b16 100644 --- a/micro-frontends/src/next-ui/utils/FormDisplayControl/FormView.spec.js +++ b/micro-frontends/src/next-ui/utils/FormDisplayControl/FormView.spec.js @@ -212,14 +212,6 @@ describe("isAbnormal", () => { }); }); -describe("formatDate", () => { - it("should format date as 'DD-MMM-YYYY'", () => { - const date = "2024-05-13"; - const result = formatDate(date); - expect(result).toBe("13-May-2024"); - }); -}); - describe("getValue", () => { it("should return formatted value for DATE type", () => { const member = { type: memberTypes.DATE, value: "2024-05-13" }; diff --git a/micro-frontends/src/next-ui/utils/utils.js b/micro-frontends/src/next-ui/utils/utils.js index 10c1677e14..8c8cdf6037 100644 --- a/micro-frontends/src/next-ui/utils/utils.js +++ b/micro-frontends/src/next-ui/utils/utils.js @@ -3,4 +3,4 @@ import { defaultDateTimeFormat } from "../constants"; export const formatDate = (value, format = defaultDateTimeFormat) => { return value ? moment(value).format(format) : value; -}; \ No newline at end of file +}; diff --git a/ui/app/common/domain/services/programService.js b/ui/app/common/domain/services/programService.js index 8dd6333c4c..2c71d3f16e 100644 --- a/ui/app/common/domain/services/programService.js +++ b/ui/app/common/domain/services/programService.js @@ -125,6 +125,10 @@ angular.module('bahmni.common.domain') return appService.getAppDescriptor().getConfigValue('disableProgramOutcomeEditOption') || false; }; + var getObservationFormsConfig = function () { + return appService.getAppDescriptor().getConfigValue('observationForms') || {}; + }; + return { getAllPrograms: getAllPrograms, enrollPatientToAProgram: enrollPatientToAProgram, @@ -137,6 +141,7 @@ angular.module('bahmni.common.domain') getEnrollmentInfoFor: getEnrollmentInfoFor, getDefaultProgram: getDefaultProgram, getProgramRedirectionConfig: getProgramRedirectionConfig, - disableProgramOutcomeEditOption: disableProgramOutcomeEditOption + disableProgramOutcomeEditOption: disableProgramOutcomeEditOption, + getObservationFormsConfig: getObservationFormsConfig }; }]); diff --git a/ui/app/common/uicontrols/programmanagement/controllers/manageProgramController.js b/ui/app/common/uicontrols/programmanagement/controllers/manageProgramController.js index 5fd6f90b47..8205eef217 100644 --- a/ui/app/common/uicontrols/programmanagement/controllers/manageProgramController.js +++ b/ui/app/common/uicontrols/programmanagement/controllers/manageProgramController.js @@ -17,6 +17,18 @@ angular.module('bahmni.common.uicontrols.programmanagment') var id = "#programEnrollmentContainer"; const defaultProgram = programService.getDefaultProgram(); const programRedirectionConfig = programService.getProgramRedirectionConfig(); + const observationFormsConfig = programService.getObservationFormsConfig() || {}; + + $scope.showFormsDisplayControl = function () { + return !_.isEmpty(observationFormsConfig); + }; + + $scope.observationFormData = { + patientUuid: $scope.patient.uuid, + showEditForActiveEncounter: true, + numberOfVisits: observationFormsConfig.numberOfVisits || 10, + hasNoHierarchy: $scope.hasNoHierarchy + }; var updateActiveProgramsList = function () { spinner.forPromise(programService.getPatientPrograms($scope.patient.uuid).then(function (programs) { diff --git a/ui/app/common/uicontrols/programmanagement/views/programEnrollment.html b/ui/app/common/uicontrols/programmanagement/views/programEnrollment.html index 1f2e928101..8ab00b2595 100644 --- a/ui/app/common/uicontrols/programmanagement/views/programEnrollment.html +++ b/ui/app/common/uicontrols/programmanagement/views/programEnrollment.html @@ -84,5 +84,12 @@

+ +
+ + +
+ - \ No newline at end of file + diff --git a/ui/app/styles/clinical/_program.scss b/ui/app/styles/clinical/_program.scss index dd7b5839f7..7918cbdcb2 100644 --- a/ui/app/styles/clinical/_program.scss +++ b/ui/app/styles/clinical/_program.scss @@ -101,6 +101,14 @@ padding: 2px 4px !important; } } + + .observation-forms-container { + margin: 30px 0; + border: 1px solid #699; + border-radius: 5px 5px 0 0; + width: 66.5%; + } + } .select-program-container{ diff --git a/ui/test/unit/common/domain/services/programService.spec.js b/ui/test/unit/common/domain/services/programService.spec.js index d57f89bb4c..52a41d87a6 100644 --- a/ui/test/unit/common/domain/services/programService.spec.js +++ b/ui/test/unit/common/domain/services/programService.spec.js @@ -672,4 +672,8 @@ describe('programService', function () { mockBackend.flush(); }); + it('test getObservationFormsConfig', function () { + expect(programService.getObservationFormsConfig()).toBeTruthy(); + }); + }); diff --git a/ui/test/unit/common/uicontrols/programmanagement/controllers/manageProgramController.spec.js b/ui/test/unit/common/uicontrols/programmanagement/controllers/manageProgramController.spec.js index b6ef46a84b..34b82b619b 100644 --- a/ui/test/unit/common/uicontrols/programmanagement/controllers/manageProgramController.spec.js +++ b/ui/test/unit/common/uicontrols/programmanagement/controllers/manageProgramController.spec.js @@ -22,7 +22,7 @@ describe("ManageProgramController", function () { _provide = $provide; programService = jasmine.createSpyObj('programService', ['getPatientPrograms', 'getAllPrograms', 'deletePatientState', 'getProgramAttributeTypes', 'updatePatientProgram', - 'getDefaultProgram', 'getProgramRedirectionConfig', 'disableProgramOutcomeEditOption']); + 'getDefaultProgram', 'getProgramRedirectionConfig', 'disableProgramOutcomeEditOption', 'getObservationFormsConfig']); programService.getDefaultProgram.and.callFake(function () { deferred = q.defer(); From 6a0195bcc55f42b664424eaa8b9203f124fbf97c Mon Sep 17 00:00:00 2001 From: Himabindu T Date: Fri, 7 Jun 2024 09:55:21 +0530 Subject: [PATCH 18/24] BAH-3844 | Add. IPD Dashboard Integration To Bahmni * Phani,Sowmya | BAH-3844 | Add new IPD Dashboard to active IPD Visit Page * Phani | Add the React IPD Dashboard to ADT Patient Dashboard (#772) * Phani | Add the new React IPD Dashboard to ADT Patient Dashboard * Phani | Update Host data * Phani | Cleanup unused imports * Phani | Add a feature toggle for ipd features * Phani | Fix Missing ADT Admit/Transfer buttons (#789) * Phani | Fix ADT Admit buttons * Phani | Refactor * Tanya| fetch provider id of the login provider * Arjun | Navigation to clinical dashboard and discharge summary tab from IPD dashboard (#802) * add. navigation for discharge summary * add. navigation to discharge summary for ipd patient * fix. test failures * handle navigations from adt to new ipd screen * fix. test failures * Merge branch 'Bahmni-IPD-master' into update-schedule-medication * Tanya| add visit uuid and visit summary to hostdata * A-1206293736141146 | Getting an error while navigating back from IPD dashboard to Medication Tab (#805) * Tanya| remove trailing comma * Kavitha | A-1206342514462673 | refactored discharge summary link to visit summaries (#816) * Abi | Fix. Active OPD visit redirection to IPD dashboard (#814) * Enable read mode for inactive ipd visit (#830) * Arjun | To hide show/hide IPD button in treatments tab (#942) * update. treatments added based on active visit type * update. logic to add IPD medications in an IPD visit * add. texts to denote different type of medication * fix. uglify build errors * update. set default behaviour as adding medication based on visit type * Arjun | update. webpack and ipd micro-frontend index.js * add. mfe.ipd dependency * Arjun | update. need visitSummary info for functioning of ipd screen * Arjun |fix. failing tests * Bindu | Fix the failing test cases * Rahul | BAH-3844 | Add. MFE DrugChartDashboard Component * Rahul | BAH-3844 | Refactor. allMedicinesConfig Call * Rahul | BAH-3844 | Add. Unit Test * [Arjun, Rahul] | BAH-3844 | Refactor. Remove Unused DrugChartDashboard * BAH-3844 | changes to switch IPD to non-IPD on medication tab * BAH-3844 | fix. redirection to IPD Dashboard from In Patient and Care view Dashboard --------- Co-authored-by: Phanindra-tw Co-authored-by: tanyaa-tw <120625142+tanyaa-tw@users.noreply.github.com> Co-authored-by: Arjun G <91885483+Arjun-Go@users.noreply.github.com> Co-authored-by: kavitha-sundararajan <90255023+kavitha-sundararajan@users.noreply.github.com> Co-authored-by: Abinaya U <77735030+abinaya-u@users.noreply.github.com> Co-authored-by: Soorya Kumaran C <90232857+SooryaKumaranC-tw@users.noreply.github.com> Co-authored-by: Arjun-Go Co-authored-by: Rahul Ramesh --- micro-frontends/src/ipd/IpdDashboard.jsx | 1 + micro-frontends/src/ipd/index.js | 19 +- ui/app/adt/app.js | 9 +- ui/app/adt/constants.js | 5 +- ui/app/adt/controllers/adtController.js | 5 + ui/app/adt/controllers/wardListController.js | 7 +- ui/app/bedmanagement/index.html | 1 + ui/app/clinical/app.js | 17 ++ .../common/controllers/visitController.js | 28 +- .../common/models/auditLogEventDetails.js | 1 + ui/app/clinical/common/views/visitIpd.html | 1 + .../controllers/addTreatmentController.js | 89 +++---- .../controllers/drugOrderHistoryController.js | 12 +- .../consultation/directives/newDrugOrders.js | 8 +- .../consultation/views/newDrugOrders.html | 6 +- .../treatmentSections/drugOrderHistory.html | 26 +- .../controllers/patientDashboardController.js | 3 - .../clinical/dashboard/views/dashboard.html | 3 +- .../allvisits/directives/visitsTable.js | 11 +- ui/app/clinical/init.js | 2 +- .../common/patient/header/views/header.html | 2 +- ui/app/i18n/clinical/locale_en.json | 3 +- .../controllers/visitHeaderController.spec.js | 2 +- .../addTreatmentController.spec.js | 240 +++++++++--------- .../drugOrderHistoryController.spec.js | 177 ++++++++++++- .../displaycontrols/visitsTable.spec.js | 4 +- 26 files changed, 443 insertions(+), 239 deletions(-) create mode 100644 ui/app/clinical/common/views/visitIpd.html diff --git a/micro-frontends/src/ipd/IpdDashboard.jsx b/micro-frontends/src/ipd/IpdDashboard.jsx index 3b5bfefcfc..9cbf8c6f7e 100644 --- a/micro-frontends/src/ipd/IpdDashboard.jsx +++ b/micro-frontends/src/ipd/IpdDashboard.jsx @@ -1,5 +1,6 @@ import PropTypes from "prop-types"; import React, { Suspense, lazy } from "react"; +import Loader from "../next-ui/Components/Loader/Loader"; export function IpdDashboard(props) { const LazyApp = lazy(() => import("@openmrs-mf/ipd/IpdDashboard")); diff --git a/micro-frontends/src/ipd/index.js b/micro-frontends/src/ipd/index.js index cd74ca75b8..ae2e690723 100644 --- a/micro-frontends/src/ipd/index.js +++ b/micro-frontends/src/ipd/index.js @@ -3,7 +3,6 @@ import { react2angular } from "react2angular"; import { IpdDashboard } from "./IpdDashboard"; -// import { DrugChartDashboard } from "./DrugChartDasboard"; import { CareViewDashboard } from "./CareViewDashboard"; angular.module("bahmni.mfe.ipd", [ @@ -14,7 +13,7 @@ angular.module("bahmni.mfe.ipd", [ "bahmni.common.domain", ]); -/** MFE component 1: IpdDashboard +/** MFE component 1: IpdDashboard *================================================= */ angular @@ -24,19 +23,7 @@ angular '' }); - -/** MFE component 2: DrugChartDashboard - *================================================= */ - -// angular -// .module("bahmni.mfe.ipd") -// .component("mfeDrugChartDashboard", react2angular(DrugChartDashboard), { -// template: -// '' -// }); - - -/** MFE component 3: CareViewDashboard +/** MFE component 2: CareViewDashboard *================================================= */ angular @@ -44,4 +31,4 @@ angular .component("mfeIpdCareViewDashboard", react2angular(CareViewDashboard), { template: '' -}); \ No newline at end of file +}); diff --git a/ui/app/adt/app.js b/ui/app/adt/app.js index 91edb89f82..5c5911bc60 100644 --- a/ui/app/adt/app.js +++ b/ui/app/adt/app.js @@ -69,18 +69,21 @@ angular.module('adt').config(['$stateProvider', '$httpProvider', '$urlRouterProv views: { 'header': { templateUrl: 'views/headerAdt.html', - controller: function ($scope) { + controller: function ($scope, appService) { $scope.showClinicalDashboardLink = true; + $scope.enableIPDFeature = appService.getAppDescriptor().getConfigValue('enableIPDFeature'); } }, 'content': { template: '' }, 'additional-header': { - templateUrl: '../common/patient/header/views/header.html' + templateUrl: '../common/patient/header/views/header.html', + controller: function ($scope, appService) { + $scope.enableIPDFeature = appService.getAppDescriptor().getConfigValue('enableIPDFeature'); + } } }, - resolve: { patientResolution: function ($stateParams, patientInitialization) { return patientInitialization($stateParams.patientUuid); diff --git a/ui/app/adt/constants.js b/ui/app/adt/constants.js index 462c84ba5b..6b087f7bb4 100644 --- a/ui/app/adt/constants.js +++ b/ui/app/adt/constants.js @@ -6,8 +6,9 @@ Bahmni.ADT = Bahmni.ADT || {}; Bahmni.ADT.Constants = (function () { return { patientsListUrl: "/patient/search", - ipdDashboard: "#/patient/{{patientUuid}}/visit/{{visitUuid}}/", - admissionLocationUrl: "/openmrs/ws/rest/v1/admissionLocation/" + ipdDashboardUrl: "#/patient/{{patientUuid}}/visit/{{visitUuid}}/", + admissionLocationUrl: "/openmrs/ws/rest/v1/admissionLocation/", + mfeIpdDashboardUrl: Bahmni.Common.Constants.hostURL + '/bahmni/clinical/#/default/patient/{{patientUuid}}/dashboard/visit/ipd/{{visitUuid}}?source=adt' }; })(); diff --git a/ui/app/adt/controllers/adtController.js b/ui/app/adt/controllers/adtController.js index ea6ad4e765..b9d023ee38 100644 --- a/ui/app/adt/controllers/adtController.js +++ b/ui/app/adt/controllers/adtController.js @@ -14,7 +14,12 @@ angular.module('bahmni.adt') $scope.defaultVisitTypeName = appService.getAppDescriptor().getConfigValue('defaultVisitType'); $scope.adtObservations = []; $scope.dashboardConfig = appService.getAppDescriptor().getConfigValue('dashboard'); + $scope.enableIPDFeature = appService.getAppDescriptor().getConfigValue('enableIPDFeature'); $scope.getAdtConceptConfig = $scope.dashboardConfig.conceptName; + $scope.hostData = { + patient: $scope.patient, + provider: $rootScope.currentProvider + }; var getVisitTypeUuid = function (visitTypeName) { var visitType = _.find(visitTypes, {name: visitTypeName}); diff --git a/ui/app/adt/controllers/wardListController.js b/ui/app/adt/controllers/wardListController.js index 68f3d8a64f..322e974b13 100644 --- a/ui/app/adt/controllers/wardListController.js +++ b/ui/app/adt/controllers/wardListController.js @@ -3,10 +3,15 @@ angular.module('bahmni.adt') .controller('WardListController', ['$scope', 'queryService', 'spinner', '$q', '$window', '$stateParams', 'appService', '$rootScope', function ($scope, queryService, spinner, $q, $window, $stateParams, appService, $rootScope) { + const enableIPDFeature = appService.getAppDescriptor().getConfigValue('enableIPDFeature'); $scope.gotoPatientDashboard = function (patientUuid, visitUuid) { var options = $.extend({}, $stateParams); $.extend(options, {patientUuid: patientUuid, visitUuid: visitUuid || null}); - $window.location = appService.getAppDescriptor().formatUrl(Bahmni.ADT.Constants.ipdDashboard, options, true); + if (enableIPDFeature) { + $window.location = appService.getAppDescriptor().formatUrl(Bahmni.ADT.Constants.mfeIpdDashboardUrl, options, true); + } else { + $window.location = appService.getAppDescriptor().formatUrl(Bahmni.ADT.Constants.ipdDashboardUrl, options, true); + } }; $scope.searchText = ''; $scope.iconAttributeConfig = appService.getAppDescriptor().getConfigValue('iconAttribute') || {}; diff --git a/ui/app/bedmanagement/index.html b/ui/app/bedmanagement/index.html index ff99ee3e33..a72417d246 100644 --- a/ui/app/bedmanagement/index.html +++ b/ui/app/bedmanagement/index.html @@ -101,6 +101,7 @@ + diff --git a/ui/app/clinical/app.js b/ui/app/clinical/app.js index 9ce34d4c52..28c75c927a 100644 --- a/ui/app/clinical/app.js +++ b/ui/app/clinical/app.js @@ -325,6 +325,23 @@ angular.module('consultation') } } }) + .state('patient.dashboard.ipdVisit', { + url: '/dashboard/visit/ipd/:visitUuid?source', + data: { + backLinks: [patientSearchBackLink] + }, + views: { + 'dashboard-content': { + templateUrl: 'common/views/visitIpd.html', + controller: 'VisitController' + } + }, + resolve: { + visitSummary: function (visitSummaryInitialization, $stateParams) { + return visitSummaryInitialization($stateParams.visitUuid); + } + } + }) .state('patient.dashboard.visit', { url: '/dashboard/visit/:visitUuid/:tab', data: { diff --git a/ui/app/clinical/common/controllers/visitController.js b/ui/app/clinical/common/controllers/visitController.js index 4d44def84e..7ceaca87ed 100644 --- a/ui/app/clinical/common/controllers/visitController.js +++ b/ui/app/clinical/common/controllers/visitController.js @@ -1,8 +1,8 @@ 'use strict'; angular.module('bahmni.clinical') - .controller('VisitController', ['$scope', '$state', '$rootScope', '$q', 'encounterService', 'clinicalAppConfigService', 'configurations', 'visitSummary', '$timeout', 'printer', 'visitConfig', 'visitHistory', '$stateParams', 'locationService', 'visitService', 'appService', 'diagnosisService', 'observationsService', 'allergyService', - function ($scope, $state, $rootScope, $q, encounterService, clinicalAppConfigService, configurations, visitSummary, $timeout, printer, visitConfig, visitHistory, $stateParams, locationService, visitService, appService, diagnosisService, observationsService, allergyService) { + .controller('VisitController', ['$scope', '$state', '$rootScope', '$q', 'encounterService', '$window', 'clinicalAppConfigService', 'configurations', 'visitSummary', '$timeout', 'printer', 'visitConfig', 'visitHistory', '$stateParams', 'locationService', 'visitService', 'appService', 'diagnosisService', 'observationsService', 'allergyService', '$location', + function ($scope, $state, $rootScope, $q, encounterService, $window, clinicalAppConfigService, configurations, visitSummary, $timeout, printer, visitConfig, visitHistory, $stateParams, locationService, visitService, appService, diagnosisService, observationsService, allergyService, $location) { var encounterTypeUuid = configurations.encounterConfig().getPatientDocumentEncounterTypeUuid(); $scope.documentsPromise = encounterService.getEncountersForEncounterType($scope.patient.uuid, encounterTypeUuid).then(function (response) { return new Bahmni.Clinical.PatientFileObservationsMapper().map(response.data.results); @@ -17,6 +17,30 @@ angular.module('bahmni.clinical') $scope.showTrends = true; $scope.patientUuid = $stateParams.patientUuid; $scope.visitUuid = $stateParams.visitUuid; + $scope.isActiveIpdVisit = $scope.visitSummary.visitType === "IPD"; + $scope.isIpdReadMode = true; + if ($scope.visitSummary.visitType === "IPD" && $scope.visitSummary.stopDateTime === null) { + $scope.isIpdReadMode = false; + } + $scope.ipdDashboard = { + hostData: { + patient: {uuid: $scope.patientUuid}, + visitSummary: $scope.visitSummary, + forDate: new Date().toUTCString(), + provider: $rootScope.currentProvider, + visitUuid: $scope.visitUuid, + isReadMode: $scope.isIpdReadMode, + source: $location.search().source + }, + hostApi: { + navigation: { + visitSummary: function () { + const visitSummaryUrl = $state.href('patient.dashboard.visit', {visitUuid: $scope.visitUuid}); + $window.open(visitSummaryUrl, '_blank'); + } + } + } + }; var tab = $stateParams.tab; var encounterTypes = visitConfig.currentTab.encounterContext ? visitConfig.currentTab.encounterContext.filterEncounterTypes : null; visitService.getVisit($scope.visitUuid, 'custom:(uuid,visitType,startDatetime,stopDatetime,encounters:(uuid,encounterDatetime,provider:(display),encounterType:(display)))').then(function (response) { diff --git a/ui/app/clinical/common/models/auditLogEventDetails.js b/ui/app/clinical/common/models/auditLogEventDetails.js index 40cbb97be4..e4c6533ab2 100644 --- a/ui/app/clinical/common/models/auditLogEventDetails.js +++ b/ui/app/clinical/common/models/auditLogEventDetails.js @@ -11,6 +11,7 @@ Bahmni.Clinical.StateNameEvenTypeMap = { "patient.dashboard.show.investigation": "VIEWED_INVESTIGATION_TAB", "patient.visit.summaryprint": "VIEWED_SUMMARY_PRINT", "patient.dashboard.visit": "VIEWED_VISIT_DASHBOARD", + "patient.dashboard.ipdVisit": "VIEWED_VISIT_DASHBOARD", "patient.dashboard.visitPrint": "VIEWED_VISIT_PRINT", "patient.dashboard.observation": "VIEWED_DASHBOARD_OBSERVATION", "patient.patientProgram.show": "VIEWED_PATIENTPROGRAM" diff --git a/ui/app/clinical/common/views/visitIpd.html b/ui/app/clinical/common/views/visitIpd.html new file mode 100644 index 0000000000..a8e2ffa3eb --- /dev/null +++ b/ui/app/clinical/common/views/visitIpd.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ui/app/clinical/consultation/controllers/addTreatmentController.js b/ui/app/clinical/consultation/controllers/addTreatmentController.js index 73f504decc..d5e28f6c12 100644 --- a/ui/app/clinical/consultation/controllers/addTreatmentController.js +++ b/ui/app/clinical/consultation/controllers/addTreatmentController.js @@ -3,10 +3,10 @@ angular.module('bahmni.clinical') .controller('AddTreatmentController', ['$scope', '$rootScope', 'contextChangeHandler', 'treatmentConfig', 'drugService', '$timeout', 'clinicalAppConfigService', 'ngDialog', '$window', 'messagingService', 'appService', 'activeDrugOrders', - 'orderSetService', '$q', 'locationService', 'spinner', '$translate', '$state', 'cdssService', 'observationsService', 'diagnosisService', + 'orderSetService', '$q', 'locationService', 'spinner', '$translate', '$state', 'cdssService', 'observationsService', 'diagnosisService', 'visitService', function ($scope, $rootScope, contextChangeHandler, treatmentConfig, drugService, $timeout, - clinicalAppConfigService, ngDialog, $window, messagingService, appService, activeDrugOrders, - orderSetService, $q, locationService, spinner, $translate, $state, cdssService, observationsService, diagnosisService) { + clinicalAppConfigService, ngDialog, $window, messagingService, appService, activeDrugOrders, + orderSetService, $q, locationService, spinner, $translate, $state, cdssService, observationsService, diagnosisService, visitService) { var DateUtil = Bahmni.Common.Util.DateUtil; var DrugOrderViewModel = Bahmni.Clinical.DrugOrderViewModel; var scrollTop = _.partial($window.scrollTo, 0, 0); @@ -19,6 +19,16 @@ angular.module('bahmni.clinical') $scope.clearButtonClicked = false; $scope.conceptSource = localStorage.getItem("conceptSource") || ""; + $scope.allMedicinesInPrescriptionAvailableForIPD = appService.getAppDescriptor().getConfigValue("allMedicinesInPrescriptionAvailableForIPD") !== null ? appService.getAppDescriptor().getConfigValue("allMedicinesInPrescriptionAvailableForIPD") : true; + var currentVisitType; + if ($scope.allMedicinesInPrescriptionAvailableForIPD) { + visitService.search( + {patient: $state.params.patientUuid, includeInactive: false, v: "custom:(uuid,visitType,startDatetime,stopDatetime,location,encounters:(uuid))"} + ).then(function (response) { + currentVisitType = response.data.results[0].visitType.display; + }); + } + $scope.getFilteredOrderSets = function (searchTerm) { if (searchTerm && searchTerm.length >= 3) { orderSetService.getOrderSetsByQuery(searchTerm).then(function (response) { @@ -54,12 +64,11 @@ angular.module('bahmni.clinical') } $scope.doseFractions = treatmentConfig.getDoseFractions(); - $scope.hideOrderSet = treatmentConfig.inputOptionsConfig.hideOrderSet; $scope.showDoseFractions = treatmentConfig.inputOptionsConfig.showDoseFractions; $scope.isDoseFractionsAvailable = function () { - return !!($scope.doseFractions && !_.isEmpty($scope.doseFractions)); + return $scope.doseFractions && !_.isEmpty($scope.doseFractions) ? true : false; }; $scope.isRuleMode = function (treatment) { @@ -80,8 +89,8 @@ angular.module('bahmni.clinical') $scope.auditOptions = function () { return appService - .getAppDescriptor() - .getConfigValue('cdssDismissalOptionsToDisplay'); + .getAppDescriptor() + .getConfigValue('cdssDismissalOptionsToDisplay'); }; $scope.submitAudit = function (index) { @@ -90,10 +99,10 @@ angular.module('bahmni.clinical') var eventType = 'Dismissed: ' + $scope.treatment.audit; $scope.newAlerts.splice(index, 1); return drugService - .cdssAudit(patientUuid, eventType, message, 'CDSS') - .then(function () { - $scope.treatment.audit = ''; - }); + .cdssAudit(patientUuid, eventType, message, 'CDSS') + .then(function () { + $scope.treatment.audit = ''; + }); }; $scope.$on('$stateChangeStart', function () { @@ -184,7 +193,7 @@ angular.module('bahmni.clinical') var existingTreatment = false; angular.forEach($scope.consultation.discontinuedDrugs, function (drugOrder) { existingTreatment = _.some($scope.treatments, function (treatment) { - return treatment.getDisplayName() === drugOrder.getDisplayName(); + return treatment.getDisplayName() === drugOrder.getDisplayName() && treatment.careSetting === drugOrder.careSetting; }) && drugOrder.isMarkedForDiscontinue; }); return existingTreatment; @@ -231,7 +240,7 @@ angular.module('bahmni.clinical') setSortWeightForOrderSetDrugs(refilledOrderGroupOrders); // Fetch the orderSet for the drugOrder - var matchedOrderSet = _.find(orderSets, {uuid: drugOrder.orderSetUuid}); + var matchedOrderSet = _.find(orderSets, { uuid: drugOrder.orderSetUuid }); // Find the drugs in ordered DrugOrderSet which matches with the matchedOrderSet SetMembers var orderSetMembersOfMatchedOrderSet = matchedOrderSet.orderSetMembers; @@ -240,13 +249,9 @@ angular.module('bahmni.clinical') _.each(refilledOrderGroupOrders, function (drugOrder) { _.each(orderSetMembersOfMatchedOrderSet, function (orderSetMember) { if (orderSetMember.orderTemplate.drug) { - if (orderSetMember.orderTemplate.drug.uuid === _.get(drugOrder, 'drug.uuid')) { - matchedMembers.push(orderSetMember); - } + if (orderSetMember.orderTemplate.drug.uuid === _.get(drugOrder, 'drug.uuid')) { matchedMembers.push(orderSetMember); } } else { - if (orderSetMember.concept.uuid === drugOrder.concept.uuid) { - matchedMembers.push(orderSetMember); - } + if (orderSetMember.concept.uuid === drugOrder.concept.uuid) { matchedMembers.push(orderSetMember); } } }); }); @@ -312,6 +317,10 @@ angular.module('bahmni.clinical') getAlerts(); }); + $scope.$on("event:updateDrugOrderType", function (event, drugOrder) { + $scope.treatments.push(drugOrder); + }); + $scope.$on("event:discontinueDrugOrder", function (event, drugOrder) { drugOrder.isMarkedForDiscontinue = true; drugOrder.isEditAllowed = false; @@ -398,6 +407,9 @@ angular.module('bahmni.clinical') ($scope.addTreatmentWithDiagnosis.hasOwnProperty('order') && $scope.confirmedDiagnoses.length == 0)) { return; } + if ($scope.allMedicinesInPrescriptionAvailableForIPD && currentVisitType === 'IPD') { + $scope.treatment.careSetting = Bahmni.Clinical.Constants.careSetting.inPatient; + } if ($scope.treatment.isNewOrderSet) { treatments = $scope.orderSetTreatments; } @@ -446,23 +458,23 @@ angular.module('bahmni.clinical') var getConflictingDrugOrder = function (newDrugOrder) { var allDrugOrders = $scope.treatments.concat($scope.orderSetTreatments); allDrugOrders = _.reject(allDrugOrders, newDrugOrder); - var unsavedNotBeingEditedOrders = _.filter(allDrugOrders, {isBeingEdited: false}); + var unsavedNotBeingEditedOrders = _.filter(allDrugOrders, { isBeingEdited: false }); var existingDrugOrders; if (newDrugOrder.isBeingEdited) { - existingDrugOrders = _.reject($scope.consultation.activeAndScheduledDrugOrders, {uuid: newDrugOrder.previousOrderUuid}); + existingDrugOrders = _.reject($scope.consultation.activeAndScheduledDrugOrders, { uuid: newDrugOrder.previousOrderUuid }); } else { existingDrugOrders = $scope.consultation.activeAndScheduledDrugOrders; } existingDrugOrders = existingDrugOrders.concat(unsavedNotBeingEditedOrders); var potentiallyOverlappingOrders = existingDrugOrders.filter(function (drugOrder) { - return (drugOrder.getDisplayName() === newDrugOrder.getDisplayName() && drugOrder.overlappingScheduledWith(newDrugOrder)); + return (drugOrder.getDisplayName() === newDrugOrder.getDisplayName() && drugOrder.overlappingScheduledWith(newDrugOrder) && newDrugOrder.careSetting === drugOrder.careSetting); }); setEffectiveDates(newDrugOrder, potentiallyOverlappingOrders); var alreadyActiveSimilarOrders = existingDrugOrders.filter(function (drugOrder) { - return (drugOrder.getDisplayName() === newDrugOrder.getDisplayName() && drugOrder.overlappingScheduledWith(newDrugOrder)); + return (drugOrder.getDisplayName() === newDrugOrder.getDisplayName() && drugOrder.overlappingScheduledWith(newDrugOrder) && newDrugOrder.careSetting === drugOrder.careSetting); }); if (alreadyActiveSimilarOrders.length > 0) { @@ -548,7 +560,6 @@ angular.module('bahmni.clinical') $scope.$on("event:removeDrugOrder", function (event, index) { $scope.treatments.splice(index, 1); - getAlerts(); }); $scope.incompleteDrugOrders = function () { @@ -565,19 +576,16 @@ angular.module('bahmni.clinical') var contextChange = function () { var errorMessages = Bahmni.Clinical.Constants.errorMessages; if (isSameDrugBeingDiscontinuedAndOrdered()) { - return { - allow: false, - errorMessage: $translate.instant(errorMessages.discontinuingAndOrderingSameDrug) - }; + return { allow: false, errorMessage: $translate.instant(errorMessages.discontinuingAndOrderingSameDrug) }; } if ($scope.incompleteDrugOrders()) { $scope.formInvalid = true; - return {allow: false}; + return { allow: false }; } if ($scope.unaddedDrugOrders()) { - return {allow: false, errorMessage: $translate.instant(errorMessages.incompleteForm)}; + return { allow: false, errorMessage: $translate.instant(errorMessages.incompleteForm) }; } - return {allow: true}; + return { allow: true }; }; var setIsNotBeingEdited = function (treatment) { @@ -642,7 +650,6 @@ angular.module('bahmni.clinical') $scope.clearForm = function () { $scope.treatment = newTreatment(); $scope.formInvalid = false; - $scope.newAlerts = []; clearHighlights(); markVariable("startNewDrugEntry"); }; @@ -669,7 +676,7 @@ angular.module('bahmni.clinical') }; $scope.toggleDrugOrderAttribute = function (orderAttribute) { - orderAttribute.value = !orderAttribute.value; + orderAttribute.value = orderAttribute.value ? false : true; }; contextChangeHandler.add(contextChange); @@ -701,7 +708,7 @@ angular.module('bahmni.clinical') $scope.consultation.newlyAddedTreatments = allTreatmentsAcrossTabs.concat(includedOrderSetTreatments); if ($scope.consultation.discontinuedDrugs) { $scope.consultation.discontinuedDrugs.forEach(function (discontinuedDrug) { - var removableOrder = _.find(activeDrugOrders, {uuid: discontinuedDrug.uuid}); + var removableOrder = _.find(activeDrugOrders, { uuid: discontinuedDrug.uuid }); if (discontinuedDrug) { removableOrder.orderReasonText = discontinuedDrug.orderReasonText; removableOrder.dateActivated = null; @@ -760,9 +767,7 @@ angular.module('bahmni.clinical') $scope.newOrderSet.name = orderSet.name; var orderSetMemberTemplates = _.map(orderSet.orderSetMembers, 'orderTemplate'); var promisesToCalculateDose = _.map(orderSetMemberTemplates, putCalculatedDose); - var returnOrderSet = function () { - return orderSet; - }; + var returnOrderSet = function () { return orderSet; }; return $q.all(promisesToCalculateDose).then(returnOrderSet); }; var createDrugOrderViewModel = function (orderTemplate) { @@ -817,7 +822,7 @@ angular.module('bahmni.clinical') }); ngDialog.open({ template: 'consultation/views/treatmentSections/conflictingOrderSet.html', - data: {'conflictingDrugOrders': conflictingDrugOrders} + data: { 'conflictingDrugOrders': conflictingDrugOrders } }); $scope.popupActive = true; }; @@ -849,18 +854,17 @@ angular.module('bahmni.clinical') drugOrder.include = false; ngDialog.open({ template: 'consultation/views/treatmentSections/conflictingOrderSet.html', - data: {'conflictingDrugOrders': [conflictingDrugOrder]} + data: { 'conflictingDrugOrders': [conflictingDrugOrder] } }); $scope.popupActive = true; } - getAlerts(); }); $scope.consultation.preSaveHandler.register("drugOrderSaveHandlerKey", saveTreatment); var mergeActiveAndScheduledWithDiscontinuedOrders = function () { _.each($scope.consultation.discontinuedDrugs, function (discontinuedDrug) { - _.remove($scope.consultation.activeAndScheduledDrugOrders, {'uuid': discontinuedDrug.uuid}); + _.remove($scope.consultation.activeAndScheduledDrugOrders, { 'uuid': discontinuedDrug.uuid }); $scope.consultation.activeAndScheduledDrugOrders.push(discontinuedDrug); }); }; @@ -868,10 +872,9 @@ angular.module('bahmni.clinical') var showRulesInMedication = function (medicationConfig) { $scope.showRulesInMedication = false; if (medicationConfig !== 'undefined' && medicationConfig.tabConfig !== 'undefined' && medicationConfig.tabConfig.allMedicationTabConfig - !== 'undefined' && medicationConfig.tabConfig.allMedicationTabConfig.orderSet !== 'undefined') { + !== 'undefined' && medicationConfig.tabConfig.allMedicationTabConfig.orderSet !== 'undefined') { if (medicationConfig.tabConfig.allMedicationTabConfig.orderSet.showRulesInMedication) { $scope.showRulesInMedication = true; - $scope.ruleUnitsMap = medicationConfig.tabConfig.allMedicationTabConfig.orderSet.dosageRuleUnitsMap || []; } } }; diff --git a/ui/app/clinical/consultation/controllers/drugOrderHistoryController.js b/ui/app/clinical/consultation/controllers/drugOrderHistoryController.js index e43ba5c975..31e5bf7833 100644 --- a/ui/app/clinical/consultation/controllers/drugOrderHistoryController.js +++ b/ui/app/clinical/consultation/controllers/drugOrderHistoryController.js @@ -12,12 +12,13 @@ angular.module('bahmni.clinical') var prescribedDrugOrders = []; $scope.dispensePrivilege = Bahmni.Clinical.Constants.dispensePrivilege; $scope.scheduledDate = DateUtil.getDateWithoutTime(DateUtil.addDays(DateUtil.now(), 1)); + var allMedicinesConfig = appService.getAppDescriptor().getConfigValue("allMedicinesInPrescriptionAvailableForIPD"); + $scope.allMedicinesInPrescriptionAvailableForIPD = allMedicinesConfig !== null ? allMedicinesConfig : true; $scope.printPrescriptionFeature = appService.getAppDescriptor().getConfigValue("printPrescriptionFeature"); $scope.autoSelectNotAllowed = $scope.printPrescriptionFeature && $scope.printPrescriptionFeature.autoSelectNotAllowed != null ? $scope.printPrescriptionFeature.autoSelectNotAllowed : false; $scope.selectedDrugs = {}; - $scope.enableIPDFeature = appService.getAppDescriptor().getConfigValue("enableIPDFeature"); - if ($scope.enableIPDFeature) { + if (!$scope.allMedicinesInPrescriptionAvailableForIPD) { $scope.updateOrderType = function (drugOrder) { var updatedDrugOrder = angular.copy(drugOrder); updatedDrugOrder.careSetting = updatedDrugOrder.careSetting === Bahmni.Clinical.Constants.careSetting.outPatient ? Bahmni.Clinical.Constants.careSetting.inPatient : Bahmni.Clinical.Constants.careSetting.outPatient; @@ -73,6 +74,7 @@ angular.module('bahmni.clinical') var refillableDrugOrders = drugOrderHistoryHelper.getRefillableDrugOrders(orderSetOrdersAndDrugOrders.drugOrders, getPreviousVisitDrugOrders(), showOnlyActive); return _(orderSetOrdersAndDrugOrders.orderSetOrders) .concat(refillableDrugOrders) + .filter(_.identity) .uniqBy('uuid') .value(); }; @@ -119,7 +121,7 @@ angular.module('bahmni.clinical') return drugOrder.uuid == selectedDrugOrder[1]; }); if (drugOrder) { - if ($scope.printPrescriptionFeature.providerAttributesForPrint !== undefined && $scope.printPrescriptionFeature.providerAttributesForPrint.length > 0) { + if ($scope.printPrescriptionFeature && $scope.printPrescriptionFeature !== undefined && $scope.printPrescriptionFeature.providerAttributesForPrint && $scope.printPrescriptionFeature.providerAttributesForPrint !== undefined && $scope.printPrescriptionFeature.providerAttributesForPrint !== null && $scope.printPrescriptionFeature.providerAttributesForPrint.length > 0) { drugOrder.provider.attributes = {}; var promise = providerService.getAttributesForProvider(drugOrder.provider.uuid); promises.push(promise); @@ -135,7 +137,7 @@ angular.module('bahmni.clinical') } }); - if ($scope.printPrescriptionFeature.providerAttributesForPrint !== undefined && $scope.printPrescriptionFeature.providerAttributesForPrint.length > 0 && $scope.printPrescriptionFeature.observationsConcepts !== undefined) { + if ($scope.printPrescriptionFeature && $scope.printPrescriptionFeature !== undefined && $scope.printPrescriptionFeature.providerAttributesForPrint && $scope.printPrescriptionFeature.providerAttributesForPrint !== undefined && $scope.printPrescriptionFeature.providerAttributesForPrint !== null && $scope.printPrescriptionFeature.providerAttributesForPrint.length > 0) { var promise = $q.all([diagnosisService.getPatientDiagnosis($stateParams.patientUuid), providerService.getAttributesForProvider($rootScope.currentProvider.uuid), observationsService.fetch($stateParams.patientUuid, $scope.printPrescriptionFeature.observationsConcepts, "latest", null, null, null, null, null)]).then(function (response) { const diagnoses = response[0].data; const dispenserAttributes = response[1].data.results; @@ -221,7 +223,7 @@ angular.module('bahmni.clinical') }); }; - $scope.enableIPDFeature && spinner.forPromise(treatmentService.getMedicationSchedulesForOrders($stateParams.patientUuid, getActiveAndPrescribedDrugOrdersUuids()).then(function (response) { + !$scope.allMedicinesInPrescriptionAvailableForIPD && spinner.forPromise(treatmentService.getMedicationSchedulesForOrders($stateParams.patientUuid, getActiveAndPrescribedDrugOrdersUuids()).then(function (response) { $scope.medicationSchedules = response.data; })); diff --git a/ui/app/clinical/consultation/directives/newDrugOrders.js b/ui/app/clinical/consultation/directives/newDrugOrders.js index ed6b89ce2d..eded5e091e 100644 --- a/ui/app/clinical/consultation/directives/newDrugOrders.js +++ b/ui/app/clinical/consultation/directives/newDrugOrders.js @@ -3,8 +3,12 @@ angular.module('bahmni.clinical') .directive('newDrugOrders', ['messagingService', function (messagingService) { var controller = function ($scope, $rootScope, appService) { - $scope.enableIPDFeature = appService.getAppDescriptor().getConfigValue("enableIPDFeature"); - if ($scope.enableIPDFeature) { + var allMedicinesConfig = appService.getAppDescriptor().getConfigValue("allMedicinesInPrescriptionAvailableForIPD"); + $scope.allMedicinesInPrescriptionAvailableForIPD = allMedicinesConfig !== null ? allMedicinesConfig : true; + if (!$scope.allMedicinesInPrescriptionAvailableForIPD) { + $rootScope.$on("event:setEncounterId", function (event, encounterId) { + $scope.encounterId = encounterId; + }); $scope.toggleCareSetting = function (newTreatment) { newTreatment.careSetting = newTreatment.careSetting === Bahmni.Clinical.Constants.careSetting.inPatient ? Bahmni.Clinical.Constants.careSetting.outPatient : Bahmni.Clinical.Constants.careSetting.inPatient; }; diff --git a/ui/app/clinical/consultation/views/newDrugOrders.html b/ui/app/clinical/consultation/views/newDrugOrders.html index 40605680f6..4aeefdd0ab 100644 --- a/ui/app/clinical/consultation/views/newDrugOrders.html +++ b/ui/app/clinical/consultation/views/newDrugOrders.html @@ -49,7 +49,7 @@

{{ ::'MEDICATION_NEW_PRESCRIPTION' | translate}}
- @@ -73,6 +73,10 @@

{{ ::'MEDICATION_NEW_PRESCRIPTION' | translate}}
| {{newTreatment.getQuantityWithUnit()}}
+
+ | {{ ::'IPD_BUTTON' | translate}} + | {{ ::'OPD_BUTTON' | translate}} +

diff --git a/ui/app/clinical/consultation/views/treatmentSections/drugOrderHistory.html b/ui/app/clinical/consultation/views/treatmentSections/drugOrderHistory.html index 444697a991..dab9b08570 100755 --- a/ui/app/clinical/consultation/views/treatmentSections/drugOrderHistory.html +++ b/ui/app/clinical/consultation/views/treatmentSections/drugOrderHistory.html @@ -6,7 +6,7 @@
-