Skip to content

Commit e48f229

Browse files
Merge branch 'master' into updateMessageTaskStates
2 parents 57e943f + 995bbf0 commit e48f229

9 files changed

Lines changed: 533 additions & 6 deletions

File tree

package-lock.json

Lines changed: 422 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
File renamed without changes.

shared-libs/rules-engine/src/target-state.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,8 @@ module.exports = {
205205
const aggregateTarget = target => {
206206
const aggregated = pick(
207207
target,
208-
['id', 'type', 'goal', 'translation_key', 'name', 'icon', 'subtitle_translation_key', 'visible']
208+
['id', 'type', 'goal', 'translation_key', 'name', 'icon', 'subtitle_translation_key', 'visible',
209+
'limit_count_to_goal']
209210
);
210211
aggregated.value = scoreTarget(target);
211212

tests/e2e/default/targets/analytics.wdio-spec.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,20 @@ describe('Targets', () => {
152152
await browser.waitUntil(async () => (await analyticsPage.noAdminTargets().isDisplayed()) === true);
153153
});
154154

155+
it('should display goal as count when limit_count_to_goal is set and count exceeds goal', async () => {
156+
const settings = await compileTargets('targets-limit-count-config.js');
157+
await utils.updateSettings(settings, { ignoreReload: true, sync: true, refresh: true, revert: true });
158+
159+
await analyticsPage.goToTargets();
160+
await commonPage.waitForLoaders();
161+
162+
// The test setup includes person contacts, so the rules engine will calculate a pass count.
163+
// goal=1 is set low enough that it will be exceeded, so limit_count_to_goal should cap the display at 1.
164+
const targets = await analyticsPage.getTargets();
165+
const activePregnancies = targets.find(t => t.title === 'Active pregnancies');
166+
expect(activePregnancies.count).to.equal('1');
167+
});
168+
155169
it('should show error message for bad config', async () => {
156170
const settings = await compileTargets('targets-error-config.js');
157171
await utils.updateSettings(settings, { ignoreReload: true, sync: true, refresh: true, revert: true });
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
module.exports = [
2+
{
3+
id: 'active-pregnancies',
4+
translation_key: 'targets.anc.active_pregnancies.title',
5+
type: 'count',
6+
goal: 1,
7+
limit_count_to_goal: true,
8+
appliesTo: 'contacts',
9+
appliesToType: ['person'],
10+
date: 'now',
11+
},
12+
];

webapp/src/css/targets.less

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,9 @@
6969
position: relative;
7070
}
7171
.count {
72+
display: flex;
73+
flex-direction: column;
74+
align-items: center;
7275
text-align: center;
7376
.number {
7477
text-align: center;
@@ -78,11 +81,7 @@
7881
padding-bottom: 0;
7982
}
8083
.goal {
81-
position: absolute;
82-
right: 0;
83-
padding-right: 20px;
8484
color: @label-color;
85-
top: 15px;
8685
p {
8786
font-size: @font-small;
8887
font-weight: 400;

webapp/src/ts/modules/analytics/analytics-targets.component.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,10 @@
3636
[aggregate]="false">
3737
</mm-analytics-targets-progress>
3838
<div class="count" *ngIf="target.type !== 'percent'">
39+
<div class="number">{{ getDisplayCount(target) | localizeNumber }}</div>
3940
<div class="goal" *ngIf="target.goal >= 0">
4041
<p>{{ 'analytics.target.monthly_goal' | translate }} {{ target.goal | localizeNumber }}</p>
4142
</div>
42-
<div class="number">{{ target.value?.pass | localizeNumber }}</div>
4343
</div>
4444
</div>
4545
<div class="heading">

webapp/src/ts/modules/analytics/analytics-targets.component.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,4 +133,11 @@ export class AnalyticsTargetsComponent implements OnInit, OnDestroy {
133133
});
134134
});
135135
}
136+
137+
getDisplayCount(target: any): number {
138+
if (target.limit_count_to_goal && target.goal >= 0 && target.value?.pass >= target.goal) {
139+
return target.goal;
140+
}
141+
return target.value?.pass;
142+
}
136143
}

webapp/tests/karma/ts/modules/analytics/analytics-targets.component.spec.ts

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import { ContactTypesService } from '@mm-services/contact-types.service';
1919
import { SettingsService } from '@mm-services/settings.service';
2020
import { TelemetryService } from '@mm-services/telemetry.service';
2121
import { Selectors } from '@mm-selectors/index';
22+
import { ResourceIconsService } from '@mm-services/resource-icons.service';
2223
import { CONTACT_TYPES } from '@medic/constants';
2324

2425
describe('AnalyticsTargetsComponent', () => {
@@ -103,6 +104,7 @@ describe('AnalyticsTargetsComponent', () => {
103104
{ provide: ContactTypesService, useValue: contactTypesService },
104105
{ provide: SettingsService, useValue: settingsService },
105106
{ provide: TelemetryService, useValue: telemetryService },
107+
{ provide: ResourceIconsService, useValue: { getImg: sinon.stub().returns('') } },
106108
]
107109
})
108110
.compileComponents()
@@ -324,4 +326,74 @@ describe('AnalyticsTargetsComponent', () => {
324326
expect(globalActions.setTitle.notCalled).to.be.true;
325327
expect(globalActions.setShowContent.calledOnceWithExactly(false)).to.be.true;
326328
}));
329+
330+
it('should display goal as count number when limit_count_to_goal is set and count exceeds goal', fakeAsync(() => {
331+
sinon.reset();
332+
rulesEngineService.isEnabled.resolves(true);
333+
rulesEngineService.fetchTargets.resolves([
334+
{ id: 'target1', type: 'count', goal: 10, limit_count_to_goal: true, value: { pass: 15, total: 15 } },
335+
]);
336+
337+
component.ngOnInit();
338+
tick(50);
339+
fixture.detectChanges();
340+
341+
expect(fixture.nativeElement.querySelector('.count .number').innerText).to.equal('10');
342+
}));
343+
344+
it('should display goal as count number when limit_count_to_goal is set and count equals goal', fakeAsync(() => {
345+
sinon.reset();
346+
rulesEngineService.isEnabled.resolves(true);
347+
rulesEngineService.fetchTargets.resolves([
348+
{ id: 'target1', type: 'count', goal: 10, limit_count_to_goal: true, value: { pass: 10, total: 10 } },
349+
]);
350+
351+
component.ngOnInit();
352+
tick(50);
353+
fixture.detectChanges();
354+
355+
expect(fixture.nativeElement.querySelector('.count .number').innerText).to.equal('10');
356+
}));
357+
358+
it('should display actual count when limit_count_to_goal is set but count is below goal', fakeAsync(() => {
359+
sinon.reset();
360+
rulesEngineService.isEnabled.resolves(true);
361+
rulesEngineService.fetchTargets.resolves([
362+
{ id: 'target1', type: 'count', goal: 10, limit_count_to_goal: true, value: { pass: 5, total: 5 } },
363+
]);
364+
365+
component.ngOnInit();
366+
tick(50);
367+
fixture.detectChanges();
368+
369+
expect(fixture.nativeElement.querySelector('.count .number').innerText).to.equal('5');
370+
}));
371+
372+
it('should display actual count when limit_count_to_goal is not set and count exceeds goal', fakeAsync(() => {
373+
sinon.reset();
374+
rulesEngineService.isEnabled.resolves(true);
375+
rulesEngineService.fetchTargets.resolves([
376+
{ id: 'target1', type: 'count', goal: 10, value: { pass: 15, total: 15 } },
377+
]);
378+
379+
component.ngOnInit();
380+
tick(50);
381+
fixture.detectChanges();
382+
383+
expect(fixture.nativeElement.querySelector('.count .number').innerText).to.equal('15');
384+
}));
385+
386+
it('should display actual count when limit_count_to_goal is set but target has no goal', fakeAsync(() => {
387+
sinon.reset();
388+
rulesEngineService.isEnabled.resolves(true);
389+
rulesEngineService.fetchTargets.resolves([
390+
{ id: 'target1', type: 'count', goal: -1, limit_count_to_goal: true, value: { pass: 15, total: 15 } },
391+
]);
392+
393+
component.ngOnInit();
394+
tick(50);
395+
fixture.detectChanges();
396+
397+
expect(fixture.nativeElement.querySelector('.count .number').innerText).to.equal('15');
398+
}));
327399
});

0 commit comments

Comments
 (0)