From 3149fd888cb91b169429b344ed8bde71c139ba6e Mon Sep 17 00:00:00 2001 From: Angie-540 <96350406+Angie-540@users.noreply.github.com> Date: Wed, 29 Nov 2023 10:14:37 +0300 Subject: [PATCH 01/13] POC-492: Modified pre-appointment to display failed phone attempts (#1681) --- .../pre-appointment-outreach.component.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/app/clinic-dashboard/general/pre-appointment-outreach/pre-appointment-outreach.component.ts b/src/app/clinic-dashboard/general/pre-appointment-outreach/pre-appointment-outreach.component.ts index d81010b4a..7bcd5431c 100644 --- a/src/app/clinic-dashboard/general/pre-appointment-outreach/pre-appointment-outreach.component.ts +++ b/src/app/clinic-dashboard/general/pre-appointment-outreach/pre-appointment-outreach.component.ts @@ -215,6 +215,11 @@ export class PreAppointmentOutreachComponent implements OnInit { width: 100, field: 'rescheduled_date' }, + { + headerName: 'No. of Failed Phone Attempts', + width: 100, + field: 'number_of_failed_phone_attempts' + }, { headerName: 'Contact Reached', width: 100, From ecd37da9ec441bc6f5cb6790dd848a5ac527fa97 Mon Sep 17 00:00:00 2001 From: Angie-540 <96350406+Angie-540@users.noreply.github.com> Date: Wed, 29 Nov 2023 12:19:53 +0300 Subject: [PATCH 02/13] POC-570:remove program snapshot tag after patient has made clinical visit (#1687) * remove program snapshot tag after patient has made clinical visit * fixed pre-appoinment-reminder tag on program snapshot * POC-570:fixed pre-appoinment-reminder tag on program snapshot * Update src/app/patient-dashboard/hiv/program-snapshot/hiv-program-snapshot.component.ts --------- Co-authored-by: Drizzentic --- .../hiv/program-snapshot/hiv-program-snapshot.component.html | 5 ++++- .../hiv/program-snapshot/hiv-program-snapshot.component.ts | 3 ++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/app/patient-dashboard/hiv/program-snapshot/hiv-program-snapshot.component.html b/src/app/patient-dashboard/hiv/program-snapshot/hiv-program-snapshot.component.html index bcde35032..a9a4f621c 100644 --- a/src/app/patient-dashboard/hiv/program-snapshot/hiv-program-snapshot.component.html +++ b/src/app/patient-dashboard/hiv/program-snapshot/hiv-program-snapshot.component.html @@ -57,7 +57,10 @@

diff --git a/src/app/patient-dashboard/hiv/program-snapshot/hiv-program-snapshot.component.ts b/src/app/patient-dashboard/hiv/program-snapshot/hiv-program-snapshot.component.ts index 050f7b703..d4150513d 100644 --- a/src/app/patient-dashboard/hiv/program-snapshot/hiv-program-snapshot.component.ts +++ b/src/app/patient-dashboard/hiv/program-snapshot/hiv-program-snapshot.component.ts @@ -299,7 +299,8 @@ export class HivProgramSnapshotComponent implements OnInit { if ( result && result.predicted_prob_disengage && - result.predicted_risk + result.predicted_risk && + result.observed_rtc_date === null ) { this.hasPredictedScore = true; this.prediction = result; From 44f627e18e967198142afef1726cad37dcd0b688 Mon Sep 17 00:00:00 2001 From: Angie-540 <96350406+Angie-540@users.noreply.github.com> Date: Thu, 30 Nov 2023 10:49:05 +0300 Subject: [PATCH 03/13] POC-495 (#1685) * customised toastr for pre-appointment rescheduled appointments * added pre-appointment banner for patients who rescheduled appointments * added customised toastr for predicted patients who rescheduled appointments * POC-495: added customised toastr for predicted patients with rescheduled appointments added customised toastr for predicted patients who rescheduled appointments POC-495: added customised toastr for predicted patients with rescheduled appointments --------- Co-authored-by: Drizzentic --- .../patient-reminder-custom.component.css | 30 +++++++++++++++++++ .../patient-reminder.component.css | 5 ++++ .../patient-reminders.component.ts | 10 ++++++- .../patient-reminders.components.html | 14 +++++++++ 4 files changed, 58 insertions(+), 1 deletion(-) diff --git a/src/app/patient-dashboard/common/patient-reminders/patient-reminder-custom.component.css b/src/app/patient-dashboard/common/patient-reminders/patient-reminder-custom.component.css index 4354e7953..8da650363 100644 --- a/src/app/patient-dashboard/common/patient-reminders/patient-reminder-custom.component.css +++ b/src/app/patient-dashboard/common/patient-reminders/patient-reminder-custom.component.css @@ -20,3 +20,33 @@ :host.toast.toast-warning { background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAGYSURBVEhL5ZSvTsNQFMbXZGICMYGYmJhAQIJAICYQPAACiSDB8AiICQQJT4CqQEwgJvYASAQCiZiYmJhAIBATCARJy+9rTsldd8sKu1M0+dLb057v6/lbq/2rK0mS/TRNj9cWNAKPYIJII7gIxCcQ51cvqID+GIEX8ASG4B1bK5gIZFeQfoJdEXOfgX4QAQg7kH2A65yQ87lyxb27sggkAzAuFhbbg1K2kgCkB1bVwyIR9m2L7PRPIhDUIXgGtyKw575yz3lTNs6X4JXnjV+LKM/m3MydnTbtOKIjtz6VhCBq4vSm3ncdrD2lk0VgUXSVKjVDJXJzijW1RQdsU7F77He8u68koNZTz8Oz5yGa6J3H3lZ0xYgXBK2QymlWWA+RWnYhskLBv2vmE+hBMCtbA7KX5drWyRT/2JsqZ2IvfB9Y4bWDNMFbJRFmC9E74SoS0CqulwjkC0+5bpcV1CZ8NMej4pjy0U+doDQsGyo1hzVJttIjhQ7GnBtRFN1UarUlH8F3xict+HY07rEzoUGPlWcjRFRr4/gChZgc3ZL2d8oAAAAASUVORK5CYII=') !important; } + +/* Custom toast class for ml custom notification type */ +::ng-deep .ngx-toastr-custom-type { + position: relative; + overflow: hidden; + margin: 0 0 6px; + padding: 15px 15px 15px 50px; + width: 300px; + border-radius: 3px 3px 3px 3px; + background-position: 15px center; + background-repeat: no-repeat; + background-size: 24px; + box-shadow: 0 0 12px #999999; + color: #271e1e; + background-color: #ffff00; + background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAGwSURBVEhLtZa9SgNBEMc9sUxxRcoUKSzSWIhXpFMhhYWFhaBg4yPYiWCXZxBLERsLRS3EQkEfwCKdjWJAwSKCgoKCcudv4O5YLrt7EzgXhiU3/4+b2ckmwVjJSpKkQ6wAi4gwhT+z3wRBcEz0yjSseUTrcRyfsHsXmD0AmbHOC9Ii8VImnuXBPglHpQ5wwSVM7sNnTG7Za4JwDdCjxyAiH3nyA2mtaTJufiDZ5dCaqlItILh1NHatfN5skvjx9Z38m69CgzuXmZgVrPIGE763Jx9qKsRozWYw6xOHdER+nn2KkO+Bb+UV5CBN6WC6QtBgbRVozrahAbmm6HtUsgtPC19tFdxXZYBOfkbmFJ1VaHA1VAHjd0pp70oTZzvR+EVrx2Ygfdsq6eu55BHYR8hlcki+n+kERUFG8BrA0BwjeAv2M8WLQBtcy+SD6fNsmnB3AlBLrgTtVW1c2QN4bVWLATaIS60J2Du5y1TiJgjSBvFVZgTmwCU+dAZFoPxGEEs8nyHC9Bwe2GvEJv2WXZb0vjdyFT4Cxk3e/kIqlOGoVLwwPevpYHT+00T+hWwXDf4AJAOUqWcDhbwAAAAASUVORK5CYII=') !important; +} + +/* Additional styles for toastr */ +.ngx-toastr-custom-type .toast-title { + font-weight: bold; +} + +.ngx-toastr-custom-type .toast-close-button { + position: absolute; + top: 0; + right: 0; + padding: 8px; + cursor: pointer; +} diff --git a/src/app/patient-dashboard/common/patient-reminders/patient-reminder.component.css b/src/app/patient-dashboard/common/patient-reminders/patient-reminder.component.css index b0af0f6c7..cdca47d99 100644 --- a/src/app/patient-dashboard/common/patient-reminders/patient-reminder.component.css +++ b/src/app/patient-dashboard/common/patient-reminders/patient-reminder.component.css @@ -1,3 +1,8 @@ patient-reminders .panel-body { padding: 0px !important; } + +:host.toast.alert-ml { + background-color: #ffff00; + border-color: #bdaaad; +} diff --git a/src/app/patient-dashboard/common/patient-reminders/patient-reminders.component.ts b/src/app/patient-dashboard/common/patient-reminders/patient-reminders.component.ts index 2c879f7b5..b5d2c0eb0 100644 --- a/src/app/patient-dashboard/common/patient-reminders/patient-reminders.component.ts +++ b/src/app/patient-dashboard/common/patient-reminders/patient-reminders.component.ts @@ -136,7 +136,15 @@ export class PatientRemindersComponent implements OnInit, OnDestroy { this.toastrConfig ); } - + if (reminder.type === 'ml') { + toast = this.toastrService.show(reminder.message, reminder.title, { + extendedTimeOut: 0, + timeOut: 0, + positionClass: 'toast-bottom-right', + closeButton: true, + toastClass: 'ngx-toastr-custom-type' + }); + } if (reminder.type === 'info') { toast = this.toastrService.info( reminder.message, diff --git a/src/app/patient-dashboard/common/patient-reminders/patient-reminders.components.html b/src/app/patient-dashboard/common/patient-reminders/patient-reminders.components.html index 2331b28d7..a0b69ee7d 100644 --- a/src/app/patient-dashboard/common/patient-reminders/patient-reminders.components.html +++ b/src/app/patient-dashboard/common/patient-reminders/patient-reminders.components.html @@ -45,4 +45,18 @@ > {{reminder.message}}

+
+ × + {{reminder.message}} +
From f6b6a4d698a0de6344e7259f66767e004ad26a96 Mon Sep 17 00:00:00 2001 From: Angie-540 <96350406+Angie-540@users.noreply.github.com> Date: Thu, 14 Dec 2023 11:19:34 +0300 Subject: [PATCH 04/13] POC-599:Add Jua Mtoto Wako to program snapshot (#1691) * POC-599:Add Jua Mtoto Wako to program snapshot * POC-599: Add JMW to program snapshot * POC-599: Add Jua Mtoto Wako to program snapshot --------- Co-authored-by: Rugut Kibet Enock --- .../hiv-program-snapshot.component.html | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/app/patient-dashboard/hiv/program-snapshot/hiv-program-snapshot.component.html b/src/app/patient-dashboard/hiv/program-snapshot/hiv-program-snapshot.component.html index a9a4f621c..520c5fd7f 100644 --- a/src/app/patient-dashboard/hiv/program-snapshot/hiv-program-snapshot.component.html +++ b/src/app/patient-dashboard/hiv/program-snapshot/hiv-program-snapshot.component.html @@ -72,6 +72,20 @@ >

+
+

+ Jua Mtoto Wako +

+

Date: Thu, 14 Dec 2023 12:10:54 +0300 Subject: [PATCH 05/13] POC-605:Remove CovidAlert from program snapshot (#1690) Co-authored-by: Rugut Kibet Enock --- .../hiv-program-snapshot.component.html | 36 ------------------- 1 file changed, 36 deletions(-) diff --git a/src/app/patient-dashboard/hiv/program-snapshot/hiv-program-snapshot.component.html b/src/app/patient-dashboard/hiv/program-snapshot/hiv-program-snapshot.component.html index 520c5fd7f..838eaba9c 100644 --- a/src/app/patient-dashboard/hiv/program-snapshot/hiv-program-snapshot.component.html +++ b/src/app/patient-dashboard/hiv/program-snapshot/hiv-program-snapshot.component.html @@ -19,42 +19,6 @@ >

-
-

- COVID-19 Assessment Status : - {{ covid19VaccinationSummary?.vaccination_status }} -

-
-
-

- COVID-19 Screening Outcome : - {{ - covid19VaccinationSummary?.covid_screening_outcome_this_visit - }} -

-
Group Summary Leader: - {{ currentLeader.person.display }} ({{ leadershipType }}), + {{ currentLeader.person.display }}, since {{ currentLeader.startDate | date: 'medium' }} @@ -108,6 +108,12 @@

Group Summary

+ + + Group Activity: + {{ groupActivity?.value }} + +
@@ -195,10 +201,23 @@ >
- OTZ Champion + Peer Leadership - Community Staff Leadership @@ -349,12 +368,21 @@
OTZ Champion + Peer Leadership = new Subject(); public editLeaderForm: FormGroup; + public validOTZProgram = false; public endDate = { date: { month: this.currentMonth, @@ -104,6 +106,10 @@ export class GroupDetailSummaryComponent implements OnInit, OnDestroy { 'landmark', this.group.attributes ); + this.groupActivity = this.communityGroupService.getGroupAttribute( + 'groupActivity', + this.group.attributes + ); this.currentLeader = this.getCurrentLeader( group.cohortLeaders, group.cohortMembers @@ -347,6 +353,10 @@ export class GroupDetailSummaryComponent implements OnInit, OnDestroy { 'landmark', this.group.attributes ); + this.groupActivity = this.communityGroupService.getGroupAttribute( + 'groupActivity', + this.group.attributes + ); this.currentLeader = this.getCurrentLeader( group.cohortLeaders, group.cohortMembers @@ -561,6 +571,7 @@ export class GroupDetailSummaryComponent implements OnInit, OnDestroy { groupProgram: { label: program['name'], value: program['uuid'] }, provider: { label: provider.person.display, value: provider.person.uuid }, address: this.landmark.value, + groupActivity: this.groupActivity.value, groupUuid: this.group.uuid, actionButtonText: 'Save Changes' }; @@ -716,6 +727,9 @@ export class GroupDetailSummaryComponent implements OnInit, OnDestroy { const sub = this.programService.getProgramByUuid(program.value).subscribe( (prog) => { this.program = prog; + if (this.program.name === 'OTZ PROGRAM') { + this.validOTZProgram = true; + } }, (error) => { console.log(error); diff --git a/src/app/group-manager/group-detail/group-detail.component.html b/src/app/group-manager/group-detail/group-detail.component.html index 68323ecd4..133b77188 100644 --- a/src/app/group-manager/group-detail/group-detail.component.html +++ b/src/app/group-manager/group-detail/group-detail.component.html @@ -240,7 +240,10 @@
{{ enrollmentErrorMessage }}
-
+
The patient is not enrolled in this program.
+
+ Patient is not eligible for OTZ enrollment +
+ Start Group Meeting >
+
+
+
+ +
+
+
+
+ + +
+ Group Activity is required + +
+
+
+
+
+ +
+ + + + + +
+

diff --git a/src/app/group-manager/group-editor/group-editor-component.ts b/src/app/group-manager/group-editor/group-editor-component.ts index 33d317591..79c3260f0 100644 --- a/src/app/group-manager/group-editor/group-editor-component.ts +++ b/src/app/group-manager/group-editor/group-editor-component.ts @@ -38,12 +38,44 @@ export class GroupEditorComponent implements OnInit { public provider: any; public address: string; public groupType: any; + public visitStartedToday: boolean; + retroVisitDate: any; + visitStartedForThisDate: boolean; + retrospectiveOn: boolean; + public visitStartedRetro: boolean; + selectedFutureGroupVisitDate: boolean; + public visitTypes = []; + selectedPastGroupVisitDate: boolean; + public currentMonth = Moment().month() + 1; + public today = { + year: Moment().year(), + month: this.currentMonth, + day: Moment().date() + }; + public groupDateCreated: any = { + date: this.today, + jsdate: new Date(), + formatted: Moment().format('YYYY-MM-DD') + }; public editType = 'create'; public actionButtonText = `${this.editType} Group`; public groupTypes: any = []; public groupPrograms: any = []; + public groupActivities: string[] = [ + 'Tailoring', + 'Art WorldTable', + 'Tennis', + 'Chess', + 'Football', + 'ReadingDarts', + 'Farming', + 'None' + ]; public groupProgram: any; + public groupActivity: any; public success = false; + public showGroupActivity = false; + public otzProgramUuid = '203571d6-a4f2-4953-9e8b-e1105e2340f5'; public message = ''; public busy = false; public providerLoading; @@ -60,6 +92,7 @@ export class GroupEditorComponent implements OnInit { this.groupUuid = state.groupUuid; this.groupName = state.groupName; this.groupProgram = state.groupProgram; + this.groupActivity = state.groupActivity; this.facility = state.facility; this.provider = state.provider; this.address = state.address; @@ -98,6 +131,12 @@ export class GroupEditorComponent implements OnInit { this.setDefaultProgram(); this.autoGenerateGroupNumber(); } + if ( + this.editType.toLowerCase() === 'edit' && + this.groupProgram.value === this.otzProgramUuid + ) { + this.showGroupActivity = true; + } this.route.parent.parent.parent.url.subscribe((urlSegment: any) => { if (!_.isEmpty(this.facilities)) { @@ -119,6 +158,14 @@ export class GroupEditorComponent implements OnInit { }); } + private autoGenerateOTZGroupNumber() { + this._communityService + .generateGroupNumber(this.facility.value ? this.facility.value : '') + .subscribe((res: any) => { + this.groupNo = res.groupNumber.replace(/DC/g, 'OTZ'); + }); + } + public setUpProviderTypeAhead() { this.providerSuggest .pipe( @@ -195,7 +242,6 @@ export class GroupEditorComponent implements OnInit { this.creatingGroup.emit(true); this.saving = true; if (this.editType.toLowerCase() === 'edit') { - console.log(this.editType); this.updateGroup(); } if (this.editType.toLowerCase() === 'create') { @@ -247,11 +293,18 @@ export class GroupEditorComponent implements OnInit { value: this.groupProgram['value'] }); } + if (this.groupActivity !== '') { + attributes.push({ + cohortAttributeType: 'groupActivity', + value: this.groupActivity + }); + } const payLoad = { name: this.groupName, description: '', location: this.facility.value, - startDate: Moment().format('YYYY-MM-DD'), + startDate: + this.groupDateCreated.formatted || Moment().format('YYYY-MM-DD'), cohortType: DEFAULT_GROUP_TYPE, groupCohort: true, attributes: attributes @@ -285,6 +338,7 @@ export class GroupEditorComponent implements OnInit { this.groupNo = ''; this.groupType = {}; this.groupProgram = {}; + this.groupActivity = {}; this.address = ''; this.provider = ''; this.success = false; @@ -441,4 +495,22 @@ export class GroupEditorComponent implements OnInit { this.facility = event; this.autoGenerateGroupNumber(); } + + public onGroupActivityChanged(event) { + this.groupActivity = event; + this.showGroupActivity = true; + } + + public changedGroupVisitDate(event) { + this.groupDateCreated = event; + } + public onProgramChanged(event) { + if (event.value === this.otzProgramUuid) { + this.autoGenerateOTZGroupNumber(); + this.showGroupActivity = true; + } else { + this.autoGenerateGroupNumber(); + this.showGroupActivity = false; + } + } } diff --git a/src/app/group-manager/group-manager-search/group-manager-search.component.html b/src/app/group-manager/group-manager-search/group-manager-search.component.html index bffb2f931..1af579c73 100644 --- a/src/app/group-manager/group-manager-search/group-manager-search.component.html +++ b/src/app/group-manager/group-manager-search/group-manager-search.component.html @@ -53,8 +53,28 @@

Group Search

margin-left: 15px; " > - View All Groups In This Facility - + View All DC Groups In This Facility + + +
diff --git a/src/app/group-manager/group-manager-search/group-manager-search.component.ts b/src/app/group-manager/group-manager-search/group-manager-search.component.ts index 964abbbc9..ac6e6982d 100644 --- a/src/app/group-manager/group-manager-search/group-manager-search.component.ts +++ b/src/app/group-manager/group-manager-search/group-manager-search.component.ts @@ -1,4 +1,11 @@ -import { Component, OnInit, OnDestroy, TemplateRef } from '@angular/core'; +import { + Component, + OnInit, + OnDestroy, + TemplateRef, + ViewChild, + AfterViewInit +} from '@angular/core'; import { CommunityGroupService } from '../../openmrs-api/community-group-resource.service'; import { ToastrFunctionService } from 'src/app/shared/services/toastr-function.service'; import * as _ from 'lodash'; @@ -17,6 +24,7 @@ import { Group } from '../../models/group.model'; import { GridOptions, RowNode } from 'ag-grid'; import { ProgramResourceService } from 'src/app/openmrs-api/program-resource.service'; import { IndividualConfig, ToastrService } from 'ngx-toastr'; +import { CohortOtzModuleResourceService } from 'src/app/etl-api/cohort-otz-module-resource.service'; @Component({ selector: 'group-manager-search', templateUrl: './group-manager-search.component.html', @@ -40,11 +48,13 @@ export class GroupManagerSearchComponent implements OnInit, OnDestroy { public routeLoading = false; fetchingGroups: boolean; previousLocationUuid: string; - columnDefs = this.generateColumns(); rowData: any; + columnDefs = this.generateColumns(); public gridOptions: GridOptions = this.getGridOptions(); public filterText = ''; hideGroupsInCurrentFacility: boolean; + public isOTZprogram = false; + public filterOTZ = ''; constructor( private groupService: CommunityGroupService, @@ -52,7 +62,8 @@ export class GroupManagerSearchComponent implements OnInit, OnDestroy { private bsModalService: BsModalService, private route: ActivatedRoute, private programResourceService: ProgramResourceService, - private toastrService: ToastrFunctionService + private toastrService: ToastrFunctionService, + private cohortOtzModuleResourceService: CohortOtzModuleResourceService ) {} ngOnInit(): void { @@ -91,6 +102,8 @@ export class GroupManagerSearchComponent implements OnInit, OnDestroy { public showGroupsInFacilty() { this.rowData = []; this.fetchingGroups = true; + this.isOTZprogram = false; + this.filterText = ''; const locationUuid = this.router.url.split('/')[2]; if (locationUuid !== this.previousLocationUuid) { this.fetchingGroups = true; @@ -100,9 +113,9 @@ export class GroupManagerSearchComponent implements OnInit, OnDestroy { this.groupsInCurrentFacility = res.map((result) => new Group(result)); this.hideGroupsInCurrentFacility = false; this.fetchingGroups = false; + this.isOTZprogram = false; this.previousLocationUuid = locationUuid; this.rowData = this.groupsInCurrentFacility; - console.log(this.rowData, 'rowData'); }); this.subscription.add(sub); } else { @@ -110,6 +123,52 @@ export class GroupManagerSearchComponent implements OnInit, OnDestroy { } } + public showOTZGroupsInFacilty() { + this.rowData = []; + this.fetchingGroups = true; + this.isOTZprogram = true; + const locationUuid = this.router.url.split('/')[2]; + this.fetchingGroups = true; + const sub = this.groupService + .getGroupsByLocationUuid(locationUuid) + .subscribe((res) => { + this.groupsInCurrentFacility = res.map((result) => { + const groupInstance = new Group(result); + const cohortUuid = this.generateCohortUuids([groupInstance]); + this.cohortOtzModuleResourceService + .getCohortSuppressionStatus(Array.from(cohortUuid.keys())) + .subscribe((supressionRate: any) => { + if (supressionRate.result.length > 0) { + groupInstance.viralSuppression = + supressionRate.result[0].suppression_rate_percentage.toFixed( + 2 + ) + '%'; + } + }); + return groupInstance; + }); + this.hideGroupsInCurrentFacility = false; + this.fetchingGroups = false; + this.isOTZprogram = false; + this.rowData = this.groupsInCurrentFacility; + this.filterText = 'OTZ PROGRAM'; + if (this.gridOptions.api) { + this.gridOptions.api.onFilterChanged(); + } + }); + + this.columnDefs = this.generateColumns(); + this.subscription.add(sub); + } + + public generateCohortUuids(cohort) { + const patientUuids = new Map(); + cohort.forEach((uuid) => { + patientUuids.set(uuid._openmrsModel.uuid, uuid._openmrsModel); + }); + return patientUuids; + } + public navigateToGroupDetails(group, newGroup?) { if (this.modalRef) { this.modalRef.hide(); @@ -162,7 +221,8 @@ export class GroupManagerSearchComponent implements OnInit, OnDestroy { return ( _.includes(node.data.display.toLowerCase(), filterCaseLowercase) || _.includes(node.data.facility.toLowerCase(), filterCaseLowercase) || - _.includes(node.data.status.toLowerCase(), filterCaseLowercase) + _.includes(node.data.status.toLowerCase(), filterCaseLowercase) || + _.includes(node.data.program, this.filterText) ); } @@ -241,6 +301,50 @@ export class GroupManagerSearchComponent implements OnInit, OnDestroy { caseSensitive: false } }, + ...(this.isOTZprogram + ? [ + { + headerName: 'Viral Suppression', + field: 'viralSuppression', + sortable: true, + filter: 'agTextColumnFilter', + width: 200, + filterParams: { + caseSensitive: false + } + }, + { + headerName: 'Last Meeting Date', + field: 'lastMeetingDate', + sortable: true, + filter: 'agTextColumnFilter', + width: 200, + filterParams: { + caseSensitive: false + } + }, + { + headerName: 'OTZ Champion', + field: 'otzChampion', + sortable: true, + filter: 'agTextColumnFilter', + width: 200, + filterParams: { + caseSensitive: false + } + }, + { + headerName: 'Group Activity', + field: 'groupActivity', + sortable: true, + filter: 'agTextColumnFilter', + width: 200, + filterParams: { + caseSensitive: false + } + } + ] + : []), { headerName: 'Actions', field: 'endDate', diff --git a/src/app/group-manager/group-manager.module.ts b/src/app/group-manager/group-manager.module.ts index b06933f05..144b1cad0 100644 --- a/src/app/group-manager/group-manager.module.ts +++ b/src/app/group-manager/group-manager.module.ts @@ -13,6 +13,7 @@ import { SuccessModalComponent } from './modals/success-modal.component'; import { GroupEditorComponent } from './group-editor/group-editor-component'; import { GroupSearchInputComponent } from './group-manager-search/group-search-input/group-search-input.component'; import { PatientSearchModule } from '../patient-search/patient-search.module'; +import { CohortOtzModuleResourceService } from '../etl-api/cohort-otz-module-resource.service'; @NgModule({ declarations: [ @@ -34,7 +35,7 @@ import { PatientSearchModule } from '../patient-search/patient-search.module'; PatientSearchModule ], exports: [GroupSearchInputComponent, GroupEditorComponent], - providers: [DatePipe], + providers: [DatePipe, CohortOtzModuleResourceService], entryComponents: [ DatePickerModalComponent, SuccessModalComponent, diff --git a/src/app/group-manager/modals/group-transfer-modal.component.ts b/src/app/group-manager/modals/group-transfer-modal.component.ts index 1d97862b0..7d4fcf1a3 100644 --- a/src/app/group-manager/modals/group-transfer-modal.component.ts +++ b/src/app/group-manager/modals/group-transfer-modal.component.ts @@ -21,9 +21,11 @@ import { Patient } from '../../models/patient.model'; @@ -42,7 +44,9 @@ export class GroupTransferModalComponent implements OnInit { constructor(public modalRef: BsModalRef) {} - ngOnInit() {} + ngOnInit() { + console.log('groupToEnroll', this.groupToUnenroll); + } confirm() { this.modalRef.hide(); diff --git a/src/app/models/group.model.ts b/src/app/models/group.model.ts index 5c205a6c8..eaecc745a 100644 --- a/src/app/models/group.model.ts +++ b/src/app/models/group.model.ts @@ -4,6 +4,7 @@ import * as _ from 'lodash'; const program_visits_config = require('../program-visit-encounter-search/program-visits-config.json'); export class Group extends BaseModel { + private _viralSuppression: string; constructor(openmrsModel?: any) { super(openmrsModel); this._openmrsModel.display = this._openmrsModel.name; @@ -38,6 +39,17 @@ export class Group extends BaseModel { @serializable() public get status() { + const lastMeetingDate = this.getLatestMeetingDate( + this._openmrsModel.cohortVisits + ); + // if last meeting date is more than 3 months ago, group is inactive + if (lastMeetingDate) { + const today = new Date(); + const threeMonthsAgo = new Date(today.setMonth(today.getMonth() - 3)); + if (lastMeetingDate < threeMonthsAgo) { + return 'Inactive'; + } + } return this._openmrsModel.endDate ? 'Disbanded' : 'Active'; } @@ -62,6 +74,34 @@ export class Group extends BaseModel { return this.getGroupMembersCount(this._openmrsModel.cohortMembers); } + @serializable() + public get otzChampion() { + const attrType = this.getCurrentLeader(this._openmrsModel.cohortLeaders); + if (attrType) { + return attrType.person.display.replace(/\d+|-/g, ''); + } + return null; + } + + @serializable() + public get groupActivity() { + const attrType = 'groupActivity'; + return this.getAttribute(attrType, this._openmrsModel.attributes); + } + + public get viralSuppression() { + return this._viralSuppression || 'Unkown %'; + } + + public set viralSuppression(value: string) { + this._viralSuppression = value; + } + + @serializable() + public get lastMeetingDate() { + return this.getLatestMeetingDate(this._openmrsModel.cohortVisits); + } + public getAttribute(attributeType, attributes) { const attr = _.filter( attributes, @@ -73,10 +113,37 @@ export class Group extends BaseModel { return null; } + public getLatestMeetingDate(startDates) { + if (startDates.length > 0) { + const latestStartDateString = startDates.reduce( + (maxDate, currentDateObject) => { + const currentStartDate = new Date(currentDateObject.startDate); + const maxStartDate = maxDate ? new Date(maxDate.startDate) : null; + + if (!maxStartDate || currentStartDate > maxStartDate) { + return currentDateObject; + } else { + return maxDate; + } + }, + null + ).startDate; + return new Date(latestStartDateString); + } + } + public getGroupMembersCount(cohortMembers) { const active_members = cohortMembers.filter( (current) => current.endDate == null ); return active_members ? active_members.length : 0; } + + public getCurrentLeader(allLeaders: any[]) { + const currentLeader = _.filter( + allLeaders, + (leader) => leader.endDate == null + )[0]; + return currentLeader; + } } diff --git a/src/app/openmrs-api/community-group-resource.service.ts b/src/app/openmrs-api/community-group-resource.service.ts index 1c3f759a2..01e928abf 100644 --- a/src/app/openmrs-api/community-group-resource.service.ts +++ b/src/app/openmrs-api/community-group-resource.service.ts @@ -12,7 +12,7 @@ export class CommunityGroupService { public cachedResults: BehaviorSubject = new BehaviorSubject([]); public v = 'full'; public _v = - 'custom:(uuid,name,description,startDate,endDate,location:(display),attributes,cohortMembers:(uuid,endDate))'; + 'custom:(uuid,name,description,cohortLeaders,startDate,endDate,location:(display),cohortVisits:(startDate),attributes,cohortMembers:(uuid,endDate))'; constructor( private http: HttpClient, diff --git a/src/app/openmrs-api/encounter-resource.service.ts b/src/app/openmrs-api/encounter-resource.service.ts index ce1b9f197..a9f6f8181 100644 --- a/src/app/openmrs-api/encounter-resource.service.ts +++ b/src/app/openmrs-api/encounter-resource.service.ts @@ -11,6 +11,7 @@ export class EncounterResourceService { 'patient:(uuid,uuid),form:(uuid,name),' + 'visit:(uuid,display,auditInfo,startDatetime,stopDatetime,location:(uuid,display)' + ',visitType:(uuid,name)),' + + 'obs:(uuid,obsDatetime,concept:(uuid,uuid,name:(display),datatype),value:ref,groupMembers),' + 'location:ref,encounterType:ref,encounterProviders:(uuid,display,provider:(uuid,display)))'; constructor( diff --git a/src/app/patient-dashboard/common/formentry/formentry.component.html b/src/app/patient-dashboard/common/formentry/formentry.component.html index fc281b3f1..f8725d48d 100644 --- a/src/app/patient-dashboard/common/formentry/formentry.component.html +++ b/src/app/patient-dashboard/common/formentry/formentry.component.html @@ -142,13 +142,20 @@
minWidth="300" closeresponsive="true" > -

+

Form submitted successfully.

+

+ + + + + Patient has been enrolled to OTZ program. +

Differentiated Care Referral @@ -261,6 +268,14 @@
> Group Manager + +
+
+ +
+
    +
  • + Last Encounter Date: + {{ patientData?.encounter_datetime }} +
  • +
  • + ARV Regimen: {{ patientData?.arv_first_regimen }} +
  • +
  • + RTC Date: {{ patientData?.rtc_date }} +
  • +
  • Module:
  • +
  • + Last Viral Load: + {{ patientData?.vl_1 | zeroVl }} +
  • +
  • + Last Viral Load Date: + + + {{ patientData?.vl_1_date | date: 'dd-MM-yyyy' }} + + +
  • +
  • + Viral Load Categorization: + + + {{ viralLoadCategory }} + + +
  • +
+
+
+ +
+ + + + + + + + + + + + + + + + + +
Latest appointmentVisit typeLatest RTC + Medication pick-up date +
+ {{ patientData?.prev_rtc_date | date: 'dd-MM-yyyy' }} + {{ patientData?.encounter_type_name }}{{ patientData?.rtc_date | date: 'dd-MM-yyyy' }} + {{ patientData?.med_pickup_rtc_date | date: 'dd-MM-yyyy' }} +
+
+
+ +
+ + + + + + + + + + + + + +
Encounter DateAdherence type
{{ patientData?.encounter_datetime }}{{ patientData?.cur_arv_adherence }}
+
+
+ +
+ + + + + + + + + + + + + + + +
DateViral LoadViral Load Category
+ {{ item.test_datetime | date: 'dd-MM-yyyy' }} + {{ item.hiv_viral_load }} + {{ + item.hiv_viral_load <= 50 + ? 'LDL' + : item.hiv_viral_load <= 200 + ? 'Low Risk Low Level Viremia' + : item.hiv_viral_load <= 500 + ? 'High Risk Low Level Viremia' + : 'Suspected Treatment Failure' + }} +
+
+
+
+ + + diff --git a/src/app/patient-dashboard/common/otz-snapshot/otz-snapshot.component.spec.ts b/src/app/patient-dashboard/common/otz-snapshot/otz-snapshot.component.spec.ts new file mode 100644 index 000000000..16e1099be --- /dev/null +++ b/src/app/patient-dashboard/common/otz-snapshot/otz-snapshot.component.spec.ts @@ -0,0 +1,24 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { OtzSnapshotComponent } from './otz-snapshot.component'; + +describe('OtzSnapshotComponent', () => { + let component: OtzSnapshotComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [OtzSnapshotComponent] + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(OtzSnapshotComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/patient-dashboard/common/otz-snapshot/otz-snapshot.component.ts b/src/app/patient-dashboard/common/otz-snapshot/otz-snapshot.component.ts new file mode 100644 index 000000000..3974c4851 --- /dev/null +++ b/src/app/patient-dashboard/common/otz-snapshot/otz-snapshot.component.ts @@ -0,0 +1,215 @@ +import { Component, OnInit } from '@angular/core'; +import { Router, ActivatedRoute } from '@angular/router'; +import { Patient } from 'src/app/models/patient.model'; +import { PatientService } from '../../services/patient.service'; +import { HivSummaryResourceService } from 'src/app/etl-api/hiv-summary-resource.service'; +import { EncounterResourceService } from 'src/app/openmrs-api/encounter-resource.service'; +import { LabsResourceService } from 'src/app/etl-api/labs-resource.service'; +import { take } from 'rxjs/operators'; +import * as moment from 'moment'; +import * as _ from 'lodash'; + +@Component({ + selector: 'app-otz-snapshot', + templateUrl: './otz-snapshot.component.html', + styleUrls: ['./otz-snapshot.component.css'] +}) +export class OtzSnapshotComponent implements OnInit { + selectedItem = 'summary'; + subscription: any; + patient: Patient; + otzEnrollment = false; + programManagerUrl: any; + groupManagerUrl: any; + otzProgramExit: any; + dateEnrolled: any; + dateCompleted: any; + loadingData: boolean; + hasLoadedData: boolean; + patientCareStatus: any; + clinicalEncounters: any; + patientData: any; + hasData: boolean; + isHEIActive: boolean; + viralLoadCategory: string; + isOtzDiscontinued = false; + reasonForDiscontinuation: string; + otzDiscontinuationDate: any; + viralLoadHistory: any[]; + isPatientEligibleForOtz = false; + + constructor( + private patientService: PatientService, + private hivSummaryResourceService: HivSummaryResourceService, + private labsResourceService: LabsResourceService, + private encounterResource: EncounterResourceService, + private router: Router, + private route: ActivatedRoute + ) {} + + ngOnInit() { + this.subscription = this.patientService.currentlyLoadedPatient.subscribe( + (patient) => { + this.patient = new Patient({}); + if (patient) { + this.isHEIActive = patient.enrolledPrograms.some((program) => { + return ( + program.programUuid === 'a8e7c30d-6d2f-401c-bb52-d4433689a36b' && + program.isEnrolled === true + ); + }); + this.programManagerUrl = + '/patient-dashboard/patient/' + + patient.uuid + + '/general/general/program-manager/new-program'; + this.otzProgramExit = + '/patient-dashboard/patient/' + + patient.uuid + + '/general/general/formentry/ab16711d-890d-4128-95ce-0e955babd711'; + this.groupManagerUrl = + '/patient-dashboard/patient/' + + patient.uuid + + '/general/general/group-enrollment'; + this.getOtzEnrollments(patient.person.age, patient.enrolledPrograms); + this.getHivSummary(patient); + this.getHistoricalPatientLabResults(patient); + this.getOtzDiscontinuation(patient); + } + } + ); + } + + selectItem(item: string) { + this.selectedItem = item; + } + + private getOtzEnrollments(age, enrolledPrograms) { + const otz = enrolledPrograms.filter( + (program) => + program.concept.uuid === 'fd90d6b2-7302-4a9c-ad1b-1f93eff77afb' + ); + if (otz.length > 0 && otz[0].isEnrolled) { + this.dateEnrolled = otz[0].dateEnrolled; + this.otzEnrollment = true; + } else { + this.dateCompleted = otz[0].dateCompleted; + } + if (age > 9 && age <= 24) { + this.isPatientEligibleForOtz = true; + } + } + + private getOtzDiscontinuation(patient) { + patient.encounters.filter((encounter) => { + console.log(encounter); + const reasonForDiscontinuation = encounter.obs.filter((obs) => { + return obs.concept.uuid === 'a89e3f94-1350-11df-a1f1-0026b9348838'; + }); + console.log(reasonForDiscontinuation); + if (reasonForDiscontinuation.length > 0) { + this.isOtzDiscontinued = true; + this.reasonForDiscontinuation = + reasonForDiscontinuation[0].value.display; + this.otzDiscontinuationDate = encounter.encounterDatetime; + } + }); + } + + public getHivSummary(patient) { + this.loadingData = true; + this.hivSummaryResourceService + .getHivSummary(patient.uuid, 0, 10, false, this.isHEIActive) + .pipe(take(1)) + .subscribe((results) => { + let latestVlResult: any; + let latestVlDate = ''; + let latestVl = null; + this.loadingData = false; + this.hasLoadedData = true; + if (results[0]) { + latestVlResult = this.getlatestVlResult(results); + latestVlDate = latestVlResult.vl_1_date; + latestVl = latestVlResult.vl_1; + this.patientCareStatus = results[0].patient_care_status; + } + this.clinicalEncounters = this.getClinicalEncounters(results); + this.patientData = _.first(this.clinicalEncounters); + const patientDataCopy = this.patientData; + + if (!_.isNil(this.patientData)) { + // assign latest vl and vl_1_date + this.patientData = Object.assign(patientDataCopy, { + vl_1_date: latestVlDate, + vl_1: latestVl + }); + this.hasData = true; + } + if (latestVl) { + this.viralLoadCategory = this.getCategory(latestVl); + } + }); + } + + private getCategory(value: number): string { + if (value <= 50) { + return 'LDL'; + } else if (value <= 200) { + return 'Low Risk Low Level Viremia'; + } else if (value <= 500) { + return 'High Risk Low Level Viremia'; + } else { + return 'Suspected Treatment Failure'; + } + } + + public getHistoricalPatientLabResults(patient) { + this.labsResourceService + .getHistoricalPatientLabResults(patient.uuid, { + startIndex: '0', + limit: '20' + }) + .pipe(take(1)) + .subscribe((results) => { + this.getViralLoadHistory(results); + }); + } + + private getViralLoadHistory(labResults: any[]): any { + const filteredArray = labResults.filter((item) => { + return item.hiv_viral_load !== null && item.test_datetime !== null; + }); + + filteredArray.sort((a, b) => { + const dateA = new Date(a.test_datetime).getTime(); + const dateB = new Date(b.test_datetime).getTime(); + return dateB - dateA; + }); + + const result = filteredArray.map((item) => { + return { + hiv_viral_load: item.hiv_viral_load, + test_datetime: item.test_datetime + }; + }); + this.viralLoadHistory = result; + } + + private getClinicalEncounters(summaries: any[]): any[] { + if (summaries) { + return _.filter(summaries, (summary: any) => { + return summary.is_clinical_encounter === 1; + }); + } + } + + private getlatestVlResult(hivSummaryData) { + const orderByVlDate = _.orderBy( + hivSummaryData, + (hivSummary) => { + return moment(hivSummary.vl_1_date); + }, + ['desc'] + ); + return orderByVlDate[0]; + } +} diff --git a/src/app/patient-dashboard/common/patient-banner/patient-banner.component.html b/src/app/patient-dashboard/common/patient-banner/patient-banner.component.html index eb55436c8..249cabc65 100644 --- a/src/app/patient-dashboard/common/patient-banner/patient-banner.component.html +++ b/src/app/patient-dashboard/common/patient-banner/patient-banner.component.html @@ -403,6 +403,23 @@ OVC ID:Not assigned + + +
border-radius: 10px; color: #31708f; font-weight: 700; + margin-bottom: 1rem; " > Create New Group @@ -51,6 +52,7 @@

Group Search

color: #31708f; font-weight: 700; margin-left: 15px; + margin-bottom: 1rem; " > View All DC Groups In This Facility @@ -59,6 +61,7 @@

Group Search

class="fa fa-spinner fa-spin" > +