Skip to content

Commit

Permalink
UHC Visits count and last visited date contact default sort (#4770)
Browse files Browse the repository at this point in the history
Adds visit count display in contacts list. Visit count display is
conditioned by the same permission as displaying last visited date.
Visits are counted based on the calendar month, with each month
starting on either the 1st or on a configurable date. Visit counters
may be color coded if a specific visit count goal configured.

Adds UHC app configuration section.
Adds UHC configuration with options to select default sorting contacts
lists by last visited date, visit counter settings for monthly count
goal and the month reset/start date.

#4758
#4752
  • Loading branch information
dianabarsan committed Aug 15, 2018
1 parent 37c6816 commit 4f41601
Show file tree
Hide file tree
Showing 16 changed files with 940 additions and 51 deletions.
1 change: 1 addition & 0 deletions admin/src/js/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ angular.module('inboxServices', []);
require('../../../webapp/src/js/services/add-attachment');
require('../../../webapp/src/js/services/auth');
require('../../../webapp/src/js/services/cache');
require('../../../webapp/src/js/services/calendar-interval');
require('../../../webapp/src/js/services/changes');
require('../../../webapp/src/js/services/contact-schema');
require('../../../webapp/src/js/services/db');
Expand Down
7 changes: 7 additions & 0 deletions api/src/config.default.json
Original file line number Diff line number Diff line change
Expand Up @@ -333,5 +333,12 @@
"reiterate_changes": true,
"changes_limit": 100,
"debounce_interval": 200
},
"uhc": {
"contacts_default_sort": "",
"visit_count": {
"month_start_date": 1,
"visit_count_goal": 0
}
}
}
5 changes: 5 additions & 0 deletions ddocs/medic-client/views/contacts_by_last_visited/map.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,10 @@ function(doc) {
} else if (doc.type === 'clinic') {
// Is a family
emit(doc._id, 0);
} else if (doc.type === 'health_center' ||
doc.type === 'district_hospital' ||
doc.type === 'person') {
// Is other contact type
emit(doc._id, -1);
}
}
4 changes: 3 additions & 1 deletion ddocs/medic/_attachments/translations/messages-en.properties
Original file line number Diff line number Diff line change
Expand Up @@ -1032,4 +1032,6 @@ contacts.results.sort.title = Sort this list
contacts.results.sort.alpha = Alphabetically
contacts.results.sort.date.last.visited = By date last visited
mrdt.disabled = The MRDT app is not available on this device
mrdt.verify = Take photo
mrdt.verify = Take photo
contacts.visits.visits = {VISITS, plural, one{visit} other{visits}}
contacts.visits.count = {{count}}
47 changes: 42 additions & 5 deletions webapp/src/css/content-list.less
Original file line number Diff line number Diff line change
Expand Up @@ -101,19 +101,16 @@
.heading {
display: flex;
justify-content: space-between;
align-items: baseline;
}

.summary {
display: flex;
justify-content: space-between;
align-items: baseline;

.overdue {
color: @high-risk-color;
&:before {
content: @high-risk-icon;
font-family: FontAwesome;
margin-right: 3px;
}
}
}

Expand All @@ -124,8 +121,48 @@
overflow: hidden;
white-space: nowrap;
}

.heading .visits {
font-size: @font-extra-extra-large;
line-height: @font-medium;
font-weight: bold;

&.warning, &.danger {
&:before {
content: @high-risk-icon;
color: @high-risk-color;
font-family: FontAwesome;
margin-right: 3px;
font-size: @font-medium;
vertical-align: middle;
}
}
}

.summary .visits {
font-size: @font-small;
font-weight: bold;
}

.visits {
&.warning {
color: @visits-goal-incomplete;
}

&.danger {
color: @high-risk-color;
}

&.success {
color: @visits-goal-done;
}
}
}
}

&.visit-count > a {
padding-right: 10px;
}
}

.action {
Expand Down
4 changes: 4 additions & 0 deletions webapp/src/css/variables.less
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@
@failed-state-color: #DA4548;
@received-state-color: #E9AA22;

/* Visit count progress colors */
@visits-goal-incomplete: #C38813;
@visits-goal-done: #538E8D;

/* icons characters */
@high-risk-icon: '\f071';
@upcoming-edd-icon: '\f111';
Expand Down
41 changes: 36 additions & 5 deletions webapp/src/js/controllers/contacts.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ var _ = require('underscore'),
Search,
SearchFilters,
Session,
Settings,
Simprints,
Tour,
TranslateFrom,
Expand All @@ -45,7 +46,7 @@ var _ = require('underscore'),
var usersHomePlace;
var additionalListItem = false;

$scope.sortDirection = 'alpha';
$scope.sortDirection = $scope.defaultSortDirection = 'alpha';

var _initScroll = function() {
scrollLoader.init(function() {
Expand Down Expand Up @@ -88,8 +89,9 @@ var _ = require('underscore'),
var extensions = {};
if ($scope.lastVisitedDateExtras) {
extensions.displayLastVisitedDate = true;
extensions.visitCountSettings = $scope.visitCountSettings;
}
if ($scope.sortDirection === 'lastVisitedDate') {
if ($scope.sortDirection === 'last_visited_date') {
extensions.sortByLastVisitedDate = true;
}

Expand Down Expand Up @@ -256,7 +258,7 @@ var _ = require('underscore'),
};
$scope.resetFilterModel = function() {
$scope.filters = {};
$scope.sortDirection = 'alpha';
$scope.sortDirection = $scope.defaultSortDirection;
SearchFilters.reset();
$scope.search();
};
Expand Down Expand Up @@ -320,13 +322,32 @@ var _ = require('underscore'),
});
};

var getVisitCountSettings = function(uhcSettings) {
if (!uhcSettings.visit_count) {
return {};
}

return {
monthStartDate: uhcSettings.visit_count.month_start_date,
visitCountGoal: uhcSettings.visit_count.visit_count_goal
};
};

var setupPromise = $q.all([
getUserHomePlaceSummary(),
canViewLastVisitedDate()
canViewLastVisitedDate(),
Settings()
])
.then(function(results) {
usersHomePlace = results[0];
$scope.lastVisitedDateExtras = results[1];
var uhcSettings = results[2] && results[2].uhc || {};
$scope.visitCountSettings = getVisitCountSettings(uhcSettings);
if ($scope.lastVisitedDateExtras &&
uhcSettings.contacts_default_sort) {
$scope.sortDirection = $scope.defaultSortDirection = uhcSettings.contacts_default_sort;
}

setActionBarData();
return $scope.search();
});
Expand All @@ -344,6 +365,15 @@ var _ = require('underscore'),
}
});

var isRelevantVisitReport = function(doc) {
return $scope.lastVisitedDateExtras &&
doc.type === 'data_record' &&
doc.form &&
doc.fields &&
doc.fields.visited_contact_uuid &&
liveList.contains({ _id: doc.fields.visited_contact_uuid });
};

var changeListener = Changes({
key: 'contacts-list',
callback: function(change) {
Expand All @@ -355,7 +385,8 @@ var _ = require('underscore'),
},
filter: function(change) {
return ContactSchema.getTypes().indexOf(change.doc.type) !== -1 ||
liveList.containsDeleteStub(change.doc);
liveList.containsDeleteStub(change.doc) ||
isRelevantVisitReport(change.doc);
}
});

Expand Down
70 changes: 70 additions & 0 deletions webapp/src/js/services/calendar-interval.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
var moment = require('moment');

(function () {

'use strict';
'ngInject';

var inboxServices = angular.module('inboxServices');

inboxServices.factory('CalendarInterval', function() {
var normalizeStartDate = function(intervalStartDate) {
intervalStartDate = parseInt(intervalStartDate);

if (isNaN(intervalStartDate) || intervalStartDate <= 0 || intervalStartDate > 31) {
intervalStartDate = 1;
}

return intervalStartDate;
};

var getMinimumStartDate = function(intervalStartDate, relativeDate) {
return moment
.min(
moment(relativeDate).subtract(1, 'month').date(intervalStartDate).startOf('day'),
moment(relativeDate).startOf('month')
)
.valueOf();
};

var getMinimumEndDate = function(intervalStartDate, nextMonth, relativeDate) {
return moment
.min(
moment(relativeDate).add(nextMonth ? 1 : 0, 'month').date(intervalStartDate - 1).endOf('day'),
moment(relativeDate).add(nextMonth ? 1 : 0, 'month').endOf('month')
)
.valueOf();
};

return {
// Returns the timestamps of the start and end of the current calendar interval
// @param {Number} [intervalStartDate=1] - day of month when interval starts (1 - 31)
//
// if `intervalStartDate` exceeds month's day count, the start/end of following/current month is returned
// f.e. `intervalStartDate` === 31 would generate next intervals :
// [12-31 -> 01-30], [01-31 -> 02-[28|29]], [03-01 -> 03-30], [03-31 -> 04-30], [05-01 -> 05-30], [05-31 - 06-30]
getCurrent: function(intervalStartDate) {
intervalStartDate = normalizeStartDate(intervalStartDate);

if (intervalStartDate === 1) {
return {
start: moment().startOf('month').valueOf(),
end: moment().endOf('month').valueOf()
};
}

if (intervalStartDate <= moment().date()) {
return {
start: moment().date(intervalStartDate).startOf('day').valueOf(),
end: getMinimumEndDate(intervalStartDate, true)
};
}

return {
start: getMinimumStartDate(intervalStartDate),
end: getMinimumEndDate(intervalStartDate)
};
}
};
});
}());
1 change: 1 addition & 0 deletions webapp/src/js/services/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
require('./android-api');
require('./auth');
require('./cache');
require('./calendar-interval');
require('./changes');
require('./check-date');
require('./child-facility');
Expand Down
18 changes: 17 additions & 1 deletion webapp/src/js/services/live-list.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ angular.module('inboxServices').factory('LiveListConfig',
scope.simprintsTier = contact.simprints && contact.simprints.tierNumber;
scope.dod = contact.date_of_death;
if (contact.type !== 'person') {
if (Number.isInteger(contact.lastVisitedDate)) {
if (Number.isInteger(contact.lastVisitedDate) && contact.lastVisitedDate >= 0) {
if (contact.lastVisitedDate === 0) {
scope.overdue = true;
scope.summary = $translate.instant('contact.last.visited.unknown');
Expand All @@ -82,6 +82,22 @@ angular.module('inboxServices').factory('LiveListConfig',
scope.overdue = contact.lastVisitedDate <= oneMonthAgo;
scope.summary = $translate.instant('contact.last.visited.date', { date: relativeDayFilter(contact.lastVisitedDate, true) });
}

var visitCount = Math.min(contact.visitCount, 99) + (contact.visitCount > 99 ? '+' : '');
scope.visits = {
count: $translate.instant('contacts.visits.count', { count: visitCount }),
summary: $translate.instant('contacts.visits.visits', { VISITS: contact.visitCount }, 'messageformat')
};

if (contact.visitCountGoal) {
if (!contact.visitCount) {
scope.visits.status = 'pending';
} else if (contact.visitCount < contact.visitCountGoal) {
scope.visits.status = 'started';
} else {
scope.visits.status = 'done';
}
}
} else {
scope.summary = $translate.instant('contact.primary_contact_name', { name: contact.contact });
}
Expand Down

0 comments on commit 4f41601

Please sign in to comment.