diff --git a/packages/openchs-android/src/action/common/AddressLevelsState.js b/packages/openchs-android/src/action/common/AddressLevelsState.js
index 00f74a63e..b6f8d4f6e 100644
--- a/packages/openchs-android/src/action/common/AddressLevelsState.js
+++ b/packages/openchs-android/src/action/common/AddressLevelsState.js
@@ -4,7 +4,10 @@ class AddressLevelsState {
constructor(levels = []) {
const unsortedLevels = Object.entries(_.uniqBy(levels, l => l.uuid)
.reduce((acc, {locationMappings, uuid, name, level, type, parentUuid, typeUuid, isSelected = false}) => {
- acc[level] = _.defaultTo(acc[level], []).concat([{
+ const accumulatorKey = level + "->" + type;
+ // accumulating just by type affects our ability to sort the levels. accumulating just by level affects our ability to group levels of the same type
+ // hence using a composite key of level + type with a separator
+ acc[accumulatorKey] = _.defaultTo(acc[accumulatorKey], []).concat([{
uuid,
name,
level,
@@ -16,8 +19,8 @@ class AddressLevelsState {
}]);
return acc;
}, {}));
- const levelTypeOrderedUnsortedLevels = _.orderBy(unsortedLevels, ([level, value]) => level, ['desc']);
- this.levels = levelTypeOrderedUnsortedLevels.map(([levelNum, levels]) => {
+ const sortedLevels = _.orderBy(unsortedLevels, ([levelKey, value]) => levelKey, ['desc']);
+ this.levels = sortedLevels.map(([levelKey, levels]) => {
const levelType = levels[0].type;
const other = _.find(levels, (level) => _.startsWith(level.name, "Other"));
if(!_.isNil(other)) {
diff --git a/packages/openchs-android/src/action/mydashboard/FiltersActions.js b/packages/openchs-android/src/action/mydashboard/FiltersActions.js
index f0b4e7c5e..83097174d 100644
--- a/packages/openchs-android/src/action/mydashboard/FiltersActions.js
+++ b/packages/openchs-android/src/action/mydashboard/FiltersActions.js
@@ -4,6 +4,7 @@ import _ from "lodash";
import FormMappingService from "../../service/FormMappingService";
import {ArrayUtil} from "openchs-models";
import AddressLevelState from '../common/AddressLevelsState';
+import General from "../../utility/General";
class FiltersActions {
@@ -64,10 +65,8 @@ class FiltersActions {
addressLevelState: action.addressLevelState
};
const addressLevelService = beans.get(AddressLevelService);
- const lowestSelectedAddressLevels = action.addressLevelState.lowestSelectedAddresses;
- const lowestAddressLevels = lowestSelectedAddressLevels
- .reduce((acc, parent) => acc.concat(addressLevelService.getDescendantsOfNode(parent)), []);
- newState.locationSearchCriteria.toggleLowestAddresses(lowestAddressLevels);
+ const toMatchAddresses = [...action.addressLevelState.selectedAddresses].concat(addressLevelService.getAllDescendants(action.addressLevelState.selectedAddresses));
+ newState.locationSearchCriteria.toggleLowestAddresses(toMatchAddresses);
return newState;
}
diff --git a/packages/openchs-android/src/action/mydashboard/MyDashboardActions.js b/packages/openchs-android/src/action/mydashboard/MyDashboardActions.js
index a1940ab98..66b7ff826 100644
--- a/packages/openchs-android/src/action/mydashboard/MyDashboardActions.js
+++ b/packages/openchs-android/src/action/mydashboard/MyDashboardActions.js
@@ -10,7 +10,6 @@ import {firebaseEvents, logEvent} from "../../utility/Analytics";
import RealmQueryService from "../../service/query/RealmQueryService";
import SubjectTypeService from "../../service/SubjectTypeService";
import {DashboardCacheFilter} from "openchs-models";
-import General from "../../utility/General";
function getApplicableEncounterTypes(holder) {
return _.isEmpty(holder.selectedGeneralEncounterTypes) ? holder.selectedEncounterTypes : holder.selectedGeneralEncounterTypes;
diff --git a/packages/openchs-android/src/service/AddressLevelService.js b/packages/openchs-android/src/service/AddressLevelService.js
index a285769b7..d28955109 100644
--- a/packages/openchs-android/src/service/AddressLevelService.js
+++ b/packages/openchs-android/src/service/AddressLevelService.js
@@ -1,6 +1,7 @@
import Service from '../framework/bean/Service';
-import {AddressLevel} from 'avni-models';
+import {AddressLevel} from 'openchs-models';
import BaseAddressLevelService from "./BaseAddressLevelService";
+import _ from "lodash";
@Service("addressLevelService")
class AddressLevelService extends BaseAddressLevelService {
@@ -11,6 +12,13 @@ class AddressLevelService extends BaseAddressLevelService {
getSchema() {
return AddressLevel.schema.name;
}
+
+ getAllDescendants(addresses) {
+ const addressLevelService = this;
+ return addresses
+ .filter(location => location.level === _.get(_.minBy(addresses, 'level'), 'level'))
+ .reduce((acc, parent) => acc.concat(addressLevelService.getDescendantsOfNode(parent)), []);
+ }
}
export default AddressLevelService;
diff --git a/packages/openchs-android/src/service/RuleEvaluationService.js b/packages/openchs-android/src/service/RuleEvaluationService.js
index aa520d997..d4ead055f 100644
--- a/packages/openchs-android/src/service/RuleEvaluationService.js
+++ b/packages/openchs-android/src/service/RuleEvaluationService.js
@@ -464,21 +464,34 @@ class RuleEvaluationService extends BaseService {
params: _.merge({visitSchedule: scheduledVisits, entity, entityContext, services: this.services}, this.getCommonParams()),
imports: getImports()
});
+ this.checkIfScheduledVisitsAreValid(nextVisits);
return nextVisits;
} catch (e) {
- General.logDebug("Rule-Failure", `New enrolment decision failed for form: ${form.uuid}`);
+ General.logDebug("Rule-Failure", `Visit Schedule failed for form: ${form.uuid}`);
this.saveFailedRules(e, form.uuid, this.getIndividualUUID(entity, entityName));
}
- } else {
+ } else if (!_.isEmpty(rulesFromTheBundle)) {
const nextVisits = rulesFromTheBundle
.reduce((schedule, rule) => {
General.logDebug(`RuleEvaluationService`, `Executing Rule: ${rule.name} Class: ${rule.fnName}`);
return this.runRuleAndSaveFailure(rule, entityName, entity, schedule, visitScheduleConfig, null, entityContext);
}, scheduledVisits);
General.logDebug("RuleEvaluationService - Next Visits", nextVisits);
- return nextVisits;
+ try {
+ this.checkIfScheduledVisitsAreValid(nextVisits);
+ return nextVisits;
+ } catch(e) {
+ General.logDebug("Rule-Failure", `Visit Schedule (old) failed for form: ${form.uuid}`);
+ this.saveFailedRules(e, form.uuid, this.getIndividualUUID(entity, entityName));
+ }
+ }
+ return defaultVisitSchedule;
+ }
+
+ checkIfScheduledVisitsAreValid(nextVisits) {
+ if (_.some(nextVisits, visit => _.isNil(visit.earliestDate))) {
+ throw new Error("Visit(s) scheduled without earliestDate");
}
- return scheduledVisits;
}
getChecklists(entity, entityName, defaultChecklists = []) {
diff --git a/packages/openchs-android/src/service/query/IndividualSearchCriteria.js b/packages/openchs-android/src/service/query/IndividualSearchCriteria.js
index 1139a1693..0c32c5891 100644
--- a/packages/openchs-android/src/service/query/IndividualSearchCriteria.js
+++ b/packages/openchs-android/src/service/query/IndividualSearchCriteria.js
@@ -98,13 +98,6 @@ class IndividualSearchCriteria {
this.allowedSubjectUUIDs = subjectUUIDs;
}
- toggleLowestAddress(lowestAddress) {
- if (BaseEntity.collectionHasEntity(this.lowestAddressLevels, lowestAddress))
- BaseEntity.removeFromCollection(this.lowestAddressLevels, lowestAddress);
- else
- this.lowestAddressLevels.push(lowestAddress);
- }
-
toggleLowestAddresses(lowestAddresses) {
this.lowestAddressLevels = lowestAddresses;
}
diff --git a/packages/openchs-android/src/service/reports/DashboardFilterService.js b/packages/openchs-android/src/service/reports/DashboardFilterService.js
index cf79dc8d5..e6b268bc1 100644
--- a/packages/openchs-android/src/service/reports/DashboardFilterService.js
+++ b/packages/openchs-android/src/service/reports/DashboardFilterService.js
@@ -80,9 +80,8 @@ class DashboardFilterService extends BaseService {
} else {
const addressLevelService = this.getService(AddressLevelService);
const addressFilterValues = [...filterValue.selectedAddresses];
- const descendants = filterValue.selectedAddresses
- .filter(location => location.level === _.get(_.minBy(filterValue.selectedAddresses, 'level'), 'level'))
- .reduce((acc, parent) => acc.concat(addressLevelService.getDescendantsOfNode(parent)), []);
+
+ const descendants = addressLevelService.getAllDescendants(filterValue.selectedAddresses);
ruleInput.filterValue = addressFilterValues.concat(descendants
.map(addressLevel => _.pick(addressLevel, ['uuid', 'name', 'level', 'type', 'parentUuid'])));
General.logDebug('DashboardFilterService', `Effective address filters: ${JSON.stringify(_.countBy(ruleInput.filterValue, "type"))}`);
diff --git a/packages/openchs-android/src/utility/General.js b/packages/openchs-android/src/utility/General.js
index b15dd54f4..3291cd4bf 100644
--- a/packages/openchs-android/src/utility/General.js
+++ b/packages/openchs-android/src/utility/General.js
@@ -202,7 +202,7 @@ class General {
}
static toISTDate(x) {
- if (x.toString().includes("18:30:00"))
+ if (x && x.toString().includes("18:30:00"))
return moment(x).add(330, "m").toDate();
return x;
}
diff --git a/packages/openchs-android/src/views/customDashboard/CardListView.js b/packages/openchs-android/src/views/customDashboard/CardListView.js
index 544d80ba3..662989e33 100644
--- a/packages/openchs-android/src/views/customDashboard/CardListView.js
+++ b/packages/openchs-android/src/views/customDashboard/CardListView.js
@@ -3,7 +3,7 @@ import React from 'react';
import Styles from '../primitives/Styles';
import {CountResult} from './CountResult';
import _, {get} from 'lodash';
-import MCIcon from 'react-native-vector-icons/MaterialCommunityIcons';
+import Colors from '../primitives/Colors';
export const CardListView = ({reportCard, I18n, onCardPress, countResult, index, isLastCard}) => {
const {name, colour, itemKey} = reportCard;
@@ -11,6 +11,7 @@ export const CardListView = ({reportCard, I18n, onCardPress, countResult, index,
const textColor = (countResult && countResult.textColor) || Styles.blackColor;
const descriptionColor = (countResult && countResult.textColor) || Styles.blackColor;
const cardColor = (countResult && countResult.cardColor) || colour || '#999999';
+ const chevronColor = Colors.darker(0.1, cardColor);
const clickable = get(countResult, 'clickable');
const renderNumber = () => {
@@ -22,6 +23,9 @@ export const CardListView = ({reportCard, I18n, onCardPress, countResult, index,
secondary={countResult.secondaryValue}
primaryStyle={[styles.primaryTextStyle, {color: textColor}, countResult.hasErrorMsg && styles.cardPrimaryTextErrorStyle]}
secondaryStyle={[styles.secondaryTextStyle, {color: textColor}, countResult.hasErrorMsg && styles.cardSecondaryTextErrorStyle]}
+ clickable={clickable}
+ chevronColor={chevronColor}
+ colour={textColor}
/>
);
};
@@ -34,10 +38,6 @@ export const CardListView = ({reportCard, I18n, onCardPress, countResult, index,
]}>
{I18n.t(cardName)}
-
- {clickable &&
- }
-
{renderNumber()}
@@ -51,7 +51,7 @@ const styles = StyleSheet.create({
rowContainer: {
flexDirection: 'row',
flexWrap: 'nowrap',
- height: 100,
+ minHeight: 100,
borderWidth: StyleSheet.hairlineWidth,
borderColor: '#DCDCDC'
},
@@ -70,17 +70,16 @@ const styles = StyleSheet.create({
},
nameContainer: {
marginLeft: 5,
- paddingHorizontal: 3,
+ paddingHorizontal: 16,
flex: 0.7,
flexDirection: 'row',
- paddingLeft: 16,
+ alignItems: 'center',
borderRightWidth: StyleSheet.hairlineWidth,
borderColor: '#DCDCDC'
},
nameTextStyle: {
- paddingTop: 15,
- fontSize: Styles.titleSize,
- width: '90%'
+ paddingVertical: 15,
+ fontSize: Styles.normalTextSize
},
numberContainer: {
flex: 0.3,
@@ -92,7 +91,8 @@ const styles = StyleSheet.create({
fontStyle: 'normal',
},
secondaryTextStyle: {
- fontSize: 16,
+ fontSize: 14,
+ fontWeight: '300',
fontStyle: 'normal',
},
cardPrimaryTextErrorStyle: {
diff --git a/packages/openchs-android/src/views/customDashboard/CardTileView.js b/packages/openchs-android/src/views/customDashboard/CardTileView.js
index b8a32da06..5bc67ef34 100644
--- a/packages/openchs-android/src/views/customDashboard/CardTileView.js
+++ b/packages/openchs-android/src/views/customDashboard/CardTileView.js
@@ -36,7 +36,7 @@ const renderNumber = function (countResult = {}, textColor) {
);
};
-const cardGap = 16;
+const cardGap = 14;
export const CardTileView = ({index, reportCard, I18n, onCardPress, countResult}) => {
const {name, colour, itemKey, iconName} = reportCard;
@@ -56,8 +56,10 @@ export const CardTileView = ({index, reportCard, I18n, onCardPress, countResult}
marginTop: cardGap,
marginLeft: index % 2 !== 0 ? cardGap : 0,
width: cardWidth,
+ minHeight: 100,
backgroundColor: cardColor,
borderColor: cardBorderColor,
+ borderWidth: 1,
paddingLeft: 16,
}]}>
@@ -69,14 +71,14 @@ export const CardTileView = ({index, reportCard, I18n, onCardPress, countResult}
{iconName && renderIcon(iconName, textColor)}
-
+
{clickable &&
- }
+ }
@@ -90,11 +92,11 @@ const styles = StyleSheet.create({
borderWidth: StyleSheet.hairlineWidth,
},
cardNameTextStyle: {
- fontSize: 18,
+ fontSize: Styles.normalTextSize,
fontStyle: 'normal'
},
cardNameContainerStyle: {
- paddingBottom: 40,
+ paddingBottom: 20,
marginRight: 12
},
cardPrimaryTextStyle: {
@@ -103,7 +105,7 @@ const styles = StyleSheet.create({
fontStyle: 'normal',
},
cardSecondaryTextStyle: {
- fontSize: 16,
+ fontSize: 14,
fontStyle: 'normal',
},
iconContainer: {
diff --git a/packages/openchs-android/src/views/customDashboard/CountResult.js b/packages/openchs-android/src/views/customDashboard/CountResult.js
index 58cf8c3ae..9e6843b2d 100644
--- a/packages/openchs-android/src/views/customDashboard/CountResult.js
+++ b/packages/openchs-android/src/views/customDashboard/CountResult.js
@@ -1,15 +1,22 @@
import React from 'react';
import {Text, View} from "react-native";
+import MCIcon from 'react-native-vector-icons/MaterialCommunityIcons';
-export const CountResult = ({primary, secondary, primaryStyle, secondaryStyle, direction}) => {
+export const CountResult = ({primary, secondary, primaryStyle, secondaryStyle, direction, clickable, chevronColor, colour}) => {
return (
{primary}
{secondary ?
- {secondary}
+ ({secondary})
:
null}
+
+
+ {clickable &&
+ }
+
)
};
diff --git a/packages/openchs-android/test/model/TestAddressLevelFactory.js b/packages/openchs-android/test/model/TestAddressLevelFactory.js
index bdd7957f8..2b2228312 100644
--- a/packages/openchs-android/test/model/TestAddressLevelFactory.js
+++ b/packages/openchs-android/test/model/TestAddressLevelFactory.js
@@ -3,11 +3,11 @@ import General from "../../src/utility/General";
import _ from 'lodash';
class TestAddressLevelFactory {
- static createWithDefaults({parent, level, name}) {
+ static createWithDefaults({parent, level, name, type}) {
const addressLevel = new AddressLevel();
addressLevel.uuid = General.randomUUID();
addressLevel.name = _.defaultTo(name, addressLevel.uuid);
- addressLevel.type = level+'_level';
+ addressLevel.type = type || level+'_level';
addressLevel.level = level;
if (!_.isNil(parent)) {
const locationMapping = new LocationMapping();
diff --git a/packages/openchs-android/test/state/AddressLevelStateTest.js b/packages/openchs-android/test/state/AddressLevelStateTest.js
index 8e769b06b..23b743809 100644
--- a/packages/openchs-android/test/state/AddressLevelStateTest.js
+++ b/packages/openchs-android/test/state/AddressLevelStateTest.js
@@ -35,4 +35,22 @@ it('should sort addressLevels by levelType and within each levelType by name', f
assert.equal(levels[0].locationMappings[0].parent.uuid, parentLevelElement[0].uuid);
}
});
+});
+
+it ('should treat multiple address level types at the same level as separate', function() {
+ const addrLevel1 = TestAddressLevelFactory.createWithDefaults({level: 1, type: 'a', name: 'location1'});
+ const addrLevel2 = TestAddressLevelFactory.createWithDefaults({level: 1, type: 'b', name: 'location2'});
+ const addrLevel3 = TestAddressLevelFactory.createWithDefaults({level: 1, type: 'c', name: 'location3'});
+
+ const allLevels = [
+ addrLevel1, addrLevel2, addrLevel3,
+ ]
+ // Shuffle the addressLevels
+ let addressLevelsState = new AddressLevelsState(_.shuffle(allLevels));
+ assert.equal(addressLevelsState.levels.length, 3);
+ addressLevelsState.levels.map(([levelType, levels]) => {
+ assert.equal(levels.length, 1); // 1 group for each levelType
+ assert.equal(levels[0].type, levelType); // group consists of same type of addressLevels
+ });
+
});
\ No newline at end of file