Skip to content

Commit a15c8ee

Browse files
latin-pandaBenmuiruri
authored andcommitted
feat(#9231): add facility indicator in aggregates targets (#9282)
1 parent 8d555e6 commit a15c8ee

File tree

1 file changed

+120
-220
lines changed

1 file changed

+120
-220
lines changed
Original file line numberDiff line numberDiff line change
@@ -1,222 +1,122 @@
1-
import { ComponentFixture, fakeAsync, flush, TestBed } from '@angular/core/testing';
2-
import { MatExpansionModule } from '@angular/material/expansion';
3-
import { TranslateFakeLoader, TranslateLoader, TranslateModule } from '@ngx-translate/core';
4-
import sinon from 'sinon';
5-
import { expect } from 'chai';
6-
import { FormsModule } from '@angular/forms';
7-
import { provideMockStore, MockStore } from '@ngrx/store/testing';
8-
9-
import { Selectors } from '@mm-selectors/index';
10-
import { AnalyticsTargetAggregatesSidebarFilterComponent }
11-
from '@mm-modules/analytics/analytics-target-aggregates-sidebar-filter.component';
12-
import { ContactTypesService } from '@mm-services/contact-types.service';
13-
import { SettingsService } from '@mm-services/settings.service';
14-
import { GlobalActions } from '@mm-actions/global';
15-
import { ReportingPeriod } from '@mm-modules/analytics/analytics-target-aggregates-sidebar-filter.component';
16-
17-
describe('Analytics Target Aggregate Sidebar Filter Component', () => {
18-
let component: AnalyticsTargetAggregatesSidebarFilterComponent;
19-
let fixture: ComponentFixture<AnalyticsTargetAggregatesSidebarFilterComponent>;
20-
let contactTypesService;
21-
let settingsService;
22-
let globalActions;
23-
let store: MockStore;
24-
25-
beforeEach(async () => {
26-
contactTypesService = {
27-
getTypeId: sinon.stub().returns('district_hospital')
28-
};
29-
settingsService = {
30-
get: sinon.stub().resolves(
31-
{ contact_types: [{ id: 'district_hospital', name_key: 'District Hospital', }] }
32-
)
33-
};
34-
globalActions = {
35-
setSidebarFilter: sinon.stub(GlobalActions.prototype, 'setSidebarFilter'),
36-
};
37-
38-
const mockedSelectors = [
39-
{ selector: Selectors.getSidebarFilter, value: {} },
40-
];
41-
42-
await TestBed
43-
.configureTestingModule({
44-
imports: [
45-
FormsModule,
46-
TranslateModule.forRoot({ loader: { provide: TranslateLoader, useClass: TranslateFakeLoader } }),
47-
MatExpansionModule
48-
],
49-
declarations: [
50-
AnalyticsTargetAggregatesSidebarFilterComponent,
51-
],
52-
providers: [
53-
provideMockStore({ selectors: mockedSelectors }),
54-
{ provide: ContactTypesService, useValue: contactTypesService },
55-
{ provide: SettingsService, useValue: settingsService },
56-
]
1+
import { Injectable } from '@angular/core';
2+
import { Place } from '@medic/cht-datasource';
3+
4+
import { CacheService } from '@mm-services/cache.service';
5+
import { GetDataRecordsService } from '@mm-services/get-data-records.service';
6+
import { DbService } from '@mm-services/db.service';
7+
import { SessionService } from '@mm-services/session.service';
8+
import { LanguageService } from '@mm-services/language.service';
9+
10+
@Injectable({
11+
providedIn: 'root'
12+
})
13+
export class UserSettingsService {
14+
private readonly cache;
15+
constructor(
16+
private cacheService: CacheService,
17+
private getDataRecordsService: GetDataRecordsService,
18+
private dbService: DbService,
19+
private languageService: LanguageService,
20+
private sessionService: SessionService,
21+
) {
22+
this.cache = this.cacheService.register({
23+
get: callback => {
24+
const docId = this.userDocId();
25+
this.dbService.get()
26+
.get(docId)
27+
.catch(() => {
28+
// might be first load - try the remote db
29+
return dbService.get({ remote: true }).get(docId);
30+
})
31+
.then(doc => {
32+
callback(null, doc);
33+
})
34+
.catch(callback);
35+
},
36+
invalidate: change => {
37+
const docId = this.userDocId();
38+
return change.id === docId;
39+
}
40+
});
41+
}
42+
43+
private userDocId() {
44+
const userCtx = this.sessionService.userCtx();
45+
if (userCtx) {
46+
return 'org.couchdb.user:' + userCtx.name;
47+
}
48+
}
49+
50+
get(): Promise<UserSettings> {
51+
const docId = this.userDocId();
52+
if (!docId) {
53+
return Promise.reject(new Error('UserCtx not found'));
54+
}
55+
56+
return new Promise((resolve, reject) => {
57+
this.cache((err, userSettings: UserSettings) => {
58+
if (err) {
59+
return reject(err);
60+
}
61+
resolve(userSettings);
62+
});
63+
});
64+
}
65+
66+
async hasMultipleFacilities(): Promise<boolean> {
67+
return this
68+
.get()
69+
.then((userSettings: UserSettings) => {
70+
const userFacility = userSettings.facility_id;
71+
return Array.isArray(userFacility) && userFacility.length > 1;
72+
});
73+
}
74+
75+
getUserFacilities(): Promise<Place.v1.Place[]> {
76+
return this
77+
.get()
78+
.then((userSettings: UserSettings) => {
79+
let userFacilities = userSettings.facility_id;
80+
if (userFacilities && !Array.isArray(userFacilities)) {
81+
userFacilities = [userFacilities];
82+
}
83+
return this.getDataRecordsService.get(userFacilities);
5784
})
58-
.compileComponents()
59-
.then(() => {
60-
fixture = TestBed.createComponent(AnalyticsTargetAggregatesSidebarFilterComponent);
61-
component = fixture.componentInstance;
62-
store = TestBed.inject(MockStore);
63-
component.userFacilities = [];
64-
fixture.detectChanges();
85+
.catch((err) => {
86+
console.error('Error fetching user facility:', err);
87+
return [];
6588
});
66-
});
67-
68-
afterEach(() => {
69-
store.resetSelectors();
70-
sinon.restore();
71-
});
72-
73-
it('should create component', fakeAsync(() => {
74-
expect(component).to.exist;
75-
expect(component.isOpen).to.be.false;
76-
}));
77-
78-
it('should unsubscribe from observables on component destroy', () => {
79-
const unsubscribeSpy = sinon.spy(component.subscriptions, 'unsubscribe');
80-
81-
component.ngOnDestroy();
82-
83-
expect(unsubscribeSpy.callCount).to.equal(1);
84-
});
85-
86-
it('should toggle sidebar filter', () => {
87-
component.toggleSidebarFilter();
88-
component.toggleSidebarFilter();
89-
component.toggleSidebarFilter();
90-
91-
expect(globalActions.setSidebarFilter.calledThrice).to.be.true;
92-
expect(globalActions.setSidebarFilter.args[0][0]).to.deep.equal({ isOpen: true });
93-
expect(globalActions.setSidebarFilter.args[1][0]).to.deep.equal({ isOpen: false });
94-
expect(globalActions.setSidebarFilter.args[2][0]).to.deep.equal({ isOpen: true });
95-
});
96-
97-
it('should set selectedFacility when default filter specified', fakeAsync(() => {
98-
sinon.resetHistory();
99-
store.overrideSelector(Selectors.getSidebarFilter, { defaultFilters: { facility: { _id: 'facility_2' } } });
100-
store.refreshState();
101-
flush();
102-
103-
expect(component.selectedFacility).to.deep.equal({ _id: 'facility_2' });
104-
105-
component.selectedFacility = null;
106-
store.overrideSelector(Selectors.getSidebarFilter, { defaultFilters: { facility: { _id: 'facility_1' } } });
107-
store.refreshState();
108-
flush();
109-
110-
expect(component.selectedFacility).to.deep.equal({ _id: 'facility_1' });
111-
}));
112-
113-
it('should not set selectedFacility when no default filter specified', fakeAsync(() => {
114-
sinon.resetHistory();
115-
store.overrideSelector(Selectors.getSidebarFilter, { defaultFilters: { facility: null } });
116-
store.refreshState();
117-
flush();
118-
119-
expect(component.selectedFacility).to.be.undefined;
120-
}));
121-
122-
it('should set user facility name_key as facilityFilterLabel, when user has multiple facilities', fakeAsync(() => {
123-
sinon.resetHistory();
124-
component.userFacilities = [
125-
{ _id: 'id_1', type: 'district_hospital' },
126-
{ _id: 'id_2', type: 'district_hospital' },
127-
{ _id: 'id_3', type: 'district_hospital' },
128-
];
129-
const settings = { contact_types: [{ id: 'district_hospital', name_key: 'District Hospital' }] };
130-
settingsService.get.resolves(settings);
131-
contactTypesService.getTypeId.returns('district_hospital');
132-
133-
component.ngOnInit();
134-
flush();
135-
136-
expect(component.facilityFilterLabel).to.equal('District Hospital');
137-
expect(contactTypesService.getTypeId.callCount).to.equal(1);
138-
expect(settingsService.get.callCount).to.equal(1);
139-
}));
140-
141-
it('should set error and default facilityFilterLabel when settingsService fails', fakeAsync(() => {
142-
sinon.resetHistory();
143-
component.userFacilities = [
144-
{ _id: 'place_1', type: 'district_hospital' },
145-
{ _id: 'place_2', type: 'district_hospital' },
146-
];
147-
settingsService.get.rejects({ some: 'err' });
148-
149-
component.ngOnInit();
150-
flush();
151-
152-
expect(component.facilityFilterLabel).to.equal(component.DEFAULT_FACILITY_LABEL);
153-
expect(settingsService.get.callCount).to.equal(1);
154-
}));
155-
156-
it('should set default facilityFilterLabel when getTypeId returns undefined', fakeAsync(() => {
157-
sinon.resetHistory();
158-
const settings = { contact_types: [{ id: 'district_hospital', name_key: 'District Hospital' }] };
159-
component.userFacilities = [
160-
{ _id: 'place_1', type: 'district_hospital' },
161-
{ _id: 'place_2', type: 'district_hospital' },
162-
];
163-
settingsService.get.resolves(settings);
164-
contactTypesService.getTypeId.returns(undefined);
165-
166-
component.ngOnInit();
167-
flush();
168-
169-
expect(component.facilityFilterLabel).to.equal(component.DEFAULT_FACILITY_LABEL);
170-
expect(settingsService.get.callCount).to.equal(1);
171-
expect(contactTypesService.getTypeId.callCount).to.equal(1);
172-
}));
173-
174-
it('should set default facilityFilterLabel when contact type is not found', fakeAsync(() => {
175-
sinon.resetHistory();
176-
const settings = { contact_types: [{ id: 'health_center', name_key: 'Health Center' }] };
177-
component.userFacilities = [
178-
{ _id: 'id_1', type: 'district_hospital' },
179-
{ _id: 'id_2', type: 'district_hospital' },
180-
];
181-
settingsService.get.resolves(settings);
182-
contactTypesService.getTypeId.returns('district_hospital');
183-
184-
component.ngOnInit();
185-
flush();
186-
187-
expect(component.facilityFilterLabel).to.equal(component.DEFAULT_FACILITY_LABEL);
188-
expect(settingsService.get.callCount).to.equal(1);
189-
expect(contactTypesService.getTypeId.callCount).to.equal(1);
190-
}));
191-
192-
it('should emit selected facility when fetchAggregateTargetsByFacility is called', () => {
193-
const facility = { _id: 'place_1', type: 'district_hospital' };
194-
const spyFacility = sinon.spy(component.facilitySelectionChanged, 'emit');
195-
196-
component.fetchAggregateTargetsByFacility(facility);
197-
198-
expect(component.selectedFacility).to.deep.equal(facility);
199-
expect(spyFacility.callCount).to.equal(1);
200-
expect(spyFacility.firstCall.args[0]).to.deep.equal(facility);
201-
});
202-
203-
it('should emit default current reporting period when fetchAggregateTargetsByReportingPeriod is called', () => {
204-
const spyReportingPeriod = sinon.spy(component.reportingPeriodSelectionChanged, 'emit');
205-
206-
component.selectedReportingPeriod = ReportingPeriod.CURRENT;
207-
component.fetchAggregateTargetsByReportingPeriod();
208-
209-
expect(spyReportingPeriod.callCount).to.equal(1);
210-
expect(spyReportingPeriod.firstCall.args[0]).to.equal(ReportingPeriod.CURRENT);
211-
});
212-
213-
it('should emit previous reporting period when toggled', () => {
214-
const spyReportingPeriod = sinon.spy(component.reportingPeriodSelectionChanged, 'emit');
215-
216-
component.selectedReportingPeriod = ReportingPeriod.PREVIOUS;
217-
component.fetchAggregateTargetsByReportingPeriod();
218-
219-
expect(spyReportingPeriod.callCount).to.equal(1);
220-
expect(spyReportingPeriod.firstCall.args[0]).to.equal(ReportingPeriod.PREVIOUS);
221-
});
222-
});
89+
}
90+
91+
async getWithLanguage(): Promise<Object> {
92+
const [userSettings, language] = await Promise.all([
93+
this.get(),
94+
this.languageService.get()
95+
]);
96+
return { ...userSettings, language };
97+
}
98+
99+
put(doc): Promise<Object> {
100+
return this.dbService
101+
.get()
102+
.put(doc);
103+
}
104+
105+
setAsKnown(): Promise<Object> {
106+
return this.get()
107+
.then((userSettings: any) => {
108+
userSettings.known = true;
109+
return this.put(userSettings);
110+
});
111+
}
112+
113+
}
114+
115+
interface UserSettings {
116+
_id: string;
117+
contact_id: string;
118+
facility_id: string[];
119+
name: string;
120+
roles: string[];
121+
type: string;
122+
}

0 commit comments

Comments
 (0)