Skip to content

Commit ef5013c

Browse files
authored
Dashboard disease burden (#298)
* Disease burden section * List and table views * Add translations
1 parent d604e4c commit ef5013c

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+1577
-118
lines changed

src/app/_constants/api.ts

+5
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,11 @@ export const API_ROUTE_EVENT_GROUPS = {
229229
DELETE: 'eventGroups/delete',
230230
};
231231

232+
export const API_ROUTE_DISEASE_BURDEN = {
233+
ENDPOINT: 'dashboard',
234+
GET_ALL: 'dashboard/diseaseBurden',
235+
};
236+
232237
export const API_ROUTE_IMMUNIZATIONS = {
233238
ENDPOINT: 'immunizations',
234239
GET_ALL: 'immunizations/indexList',

src/app/_constants/icons.ts

+3
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ export enum IconsMap {
99
false = 'not_interested',
1010
EMAIL = 'mail_outline',
1111
PHONE = 'call',
12+
RISING = 'keyboard_arrow_up',
13+
FALLING = 'keyboard_arrow_down',
14+
STAGNANT = 'keyboard_arrow_right',
1215
}
1316

1417
export enum CaseOutcomeIcons {

src/app/_models/cardAppearance.ts

+1
Original file line numberDiff line numberDiff line change
@@ -34,4 +34,5 @@ export type CardType =
3434
| 'therapy_prescriptions'
3535
| 'sample_pathogen'
3636
| 'sample_additional'
37+
| 'disease'
3738
| undefined;

src/app/_models/common.ts

+7
Original file line numberDiff line numberDiff line change
@@ -116,3 +116,10 @@ export const FormGroupStyleType = {
116116
CARD: 'CARD' as FormGroupStyle,
117117
COLLAPSABLE: 'COLLAPSABLE' as FormGroupStyle,
118118
};
119+
120+
export type ViewOptions = 'PRIMARY' | 'SECONDARY';
121+
122+
export const VIEW_OPTIONS = {
123+
PRIMARY: 'PRIMARY' as ViewOptions,
124+
SECONDARY: 'SECONDARY' as ViewOptions,
125+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
/* eslint-disable no-param-reassign */
2+
import { Serializer } from './base.serializer';
3+
import { deserializeDates, serializeDates } from './date-parse';
4+
5+
export class DiseaseBurdenSerializer implements Serializer {
6+
fromJson(json: any): any {
7+
json.id = json.id ?? json.uuid;
8+
deserializeDates(json);
9+
return json;
10+
}
11+
12+
toJson(item: any): any {
13+
serializeDates(item);
14+
return item;
15+
}
16+
}

src/app/_services/api/dashboard.service.ts

+1-9
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,9 @@
11
import { Inject, Injectable } from '@angular/core';
22
import { HttpClient } from '@angular/common/http';
33
import { Observable } from 'rxjs';
4-
54
import { map } from 'rxjs/operators';
6-
75
import { Resource } from '../../_models/resource';
86
import { Serializer } from '../../_serializers/base.serializer';
9-
107
import { HelperService } from '../helper.service';
118

129
@Injectable({
@@ -22,12 +19,7 @@ export class DashboardService {
2219
) {}
2320

2421
getCalculated(filters?: any): Observable<Resource> {
25-
// endpoint
26-
let endpoint = this.endpoint.ENDPOINT;
27-
if (this.endpoint.GET_ALL) {
28-
endpoint = this.endpoint.GET_ALL;
29-
}
30-
22+
const endpoint = this.endpoint.GET_ALL || this.endpoint.ENDPOINT;
3123
const requestPayload: any = {};
3224

3325
if (filters.length) {
+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { Injectable } from '@angular/core';
2+
import { HttpClient } from '@angular/common/http';
3+
import * as constants from '../../app.constants';
4+
import { HelperService } from '../helper.service';
5+
import { DashboardService } from './dashboard.service';
6+
import { DiseaseBurdenSerializer } from '../../_serializers/disease-burden.serializer';
7+
8+
@Injectable({
9+
providedIn: 'root',
10+
})
11+
export class DiseaseBurdenService extends DashboardService {
12+
constructor(protected httpClient: HttpClient, protected helperService: HelperService) {
13+
super(
14+
httpClient,
15+
helperService,
16+
'',
17+
constants.API_ROUTE_DISEASE_BURDEN,
18+
new DiseaseBurdenSerializer()
19+
);
20+
}
21+
}
Original file line numberDiff line numberDiff line change
@@ -1 +1,13 @@
1-
<p>dashboard-difference-cases works!</p>
1+
<section>
2+
<h2>{{ 'captions.dashboardDiseaseDifference' | translate }}</h2>
3+
<p>
4+
{{
5+
'captions.dashboardComparedToPreviousPeriod'
6+
| translate
7+
| replace: '%s':period
8+
| replace: '%s':previousPeriod
9+
}}
10+
</p>
11+
12+
<div>graph here</div>
13+
</section>
Original file line numberDiff line numberDiff line change
@@ -1 +1,11 @@
1-
/* stylelint-disable */
1+
@use '/src/variables.scss' as *;
2+
3+
h2 {
4+
font-size: 16px;
5+
line-height: 1;
6+
margin-bottom: 4px;
7+
}
8+
9+
p {
10+
color: map-get($sormas-accent, 500);
11+
}

src/app/dashboard/components/dashboard-difference-cases/dashboard-difference-cases.component.spec.ts

+3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
import { ComponentFixture, TestBed } from '@angular/core/testing';
2+
import { TranslateModule } from '@ngx-translate/core';
3+
import { PipesModule } from '../../../_pipes/pipes.module';
24

35
import { DashboardDifferenceCasesComponent } from './dashboard-difference-cases.component';
46

@@ -9,6 +11,7 @@ describe('DashboardDifferenceCasesComponent', () => {
911
beforeEach(async () => {
1012
await TestBed.configureTestingModule({
1113
declarations: [DashboardDifferenceCasesComponent],
14+
imports: [TranslateModule.forRoot(), PipesModule],
1215
}).compileComponents();
1316
});
1417

Original file line numberDiff line numberDiff line change
@@ -1,8 +1,42 @@
1-
import { Component } from '@angular/core';
1+
import { Component, Input } from '@angular/core';
2+
import { format, isSameDay } from 'date-fns';
3+
import { COMMON_DATE_FORMAT } from '../../../app.constants';
4+
import { Filter } from '../../../_models/common';
25

36
@Component({
47
selector: 'app-dashboard-difference-cases',
58
templateUrl: './dashboard-difference-cases.component.html',
69
styleUrls: ['./dashboard-difference-cases.component.scss'],
710
})
8-
export class DashboardDifferenceCasesComponent {}
11+
export class DashboardDifferenceCasesComponent {
12+
@Input() data: any;
13+
private _filters: Filter[];
14+
@Input() set filters(raw: Filter[]) {
15+
this._filters = raw;
16+
this.setPeriod();
17+
}
18+
get filters(): Filter[] {
19+
return this._filters;
20+
}
21+
22+
period: string;
23+
previousPeriod: string;
24+
25+
setPeriod(): void {
26+
const dateFrom = this.filters.find((item) => item.field === 'dateFrom')?.value;
27+
const dateTo = this.filters.find((item) => item.field === 'dateTo')?.value;
28+
const previousDateFrom = this.filters.find((item) => item.field === 'previousDateFrom')?.value;
29+
const previousDateTo = this.filters.find((item) => item.field === 'previousDateTo')?.value;
30+
this.period =
31+
!dateTo || isSameDay(dateFrom, dateTo)
32+
? format(dateFrom, COMMON_DATE_FORMAT)
33+
: `${format(dateFrom, COMMON_DATE_FORMAT)} - ${format(dateTo, COMMON_DATE_FORMAT)}`;
34+
this.previousPeriod =
35+
!previousDateTo || isSameDay(previousDateFrom, previousDateTo)
36+
? format(previousDateFrom, COMMON_DATE_FORMAT)
37+
: `${format(previousDateFrom, COMMON_DATE_FORMAT)} - ${format(
38+
previousDateTo,
39+
COMMON_DATE_FORMAT
40+
)}`;
41+
}
42+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<ng-container *ngIf="data">
2+
<div [ngClass]="visible ? 'hide-section' : 'show-section'">
3+
<mat-checkbox color="primary" class="small" (change)="toggleSection($event)">
4+
{{ 'captions.dashboardHideOverview' | translate }}
5+
</mat-checkbox>
6+
</div>
7+
8+
<mat-card *ngIf="visible">
9+
<div [ngClass]="{ grid: view.value === viewOptions.PRIMARY }">
10+
<app-dashboard-disease-overview
11+
[data]="view.value === viewOptions.PRIMARY ? data.slice(0, 6) : data"
12+
class="overview"
13+
></app-dashboard-disease-overview>
14+
<app-dashboard-difference-cases
15+
[data]="view.value === viewOptions.PRIMARY ? data.slice(0, 6) : data"
16+
[filters]="filters"
17+
class="graph"
18+
></app-dashboard-difference-cases>
19+
</div>
20+
<div [hidden]="data.length <= 6" class="expand-view">
21+
<app-toggle-view
22+
#view
23+
iconViewPrimary="expand_less_black"
24+
iconViewSecondary="expand_more_black"
25+
iconSize="large"
26+
></app-toggle-view>
27+
</div>
28+
</mat-card>
29+
</ng-container>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
@use '/src/variables.scss' as *;
2+
3+
:host {
4+
display: block;
5+
position: relative;
6+
}
7+
8+
.mat-card {
9+
padding: 24px;
10+
margin-bottom: 16px;
11+
z-index: 1;
12+
}
13+
14+
.overview,
15+
.graph {
16+
display: block;
17+
margin-bottom: 24px;
18+
}
19+
20+
.show-section {
21+
padding-right: 24px;
22+
margin-top: -8px;
23+
margin-bottom: 8px;
24+
text-align: right;
25+
}
26+
27+
.hide-section {
28+
position: absolute;
29+
top: 20px;
30+
right: 24px;
31+
z-index: 2;
32+
33+
.mat-label {
34+
font-weight: normal;
35+
}
36+
}
37+
38+
.expand-view {
39+
margin-bottom: -16px !important;
40+
text-align: center;
41+
}
42+
43+
@media (min-width: map-get($grid-breakpoints, xl)) {
44+
.grid {
45+
display: grid;
46+
grid-template-columns: 40% 60%;
47+
column-gap: 24px;
48+
}
49+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { HttpClientTestingModule } from '@angular/common/http/testing';
2+
import { ComponentFixture, TestBed } from '@angular/core/testing';
3+
import { MaterialModule } from '../../../material.module';
4+
5+
import { DashboardDiseaseBurdenComponent } from './dashboard-disease-burden.component';
6+
7+
describe('DashboardDiseaseBurdenComponent', () => {
8+
let component: DashboardDiseaseBurdenComponent;
9+
let fixture: ComponentFixture<DashboardDiseaseBurdenComponent>;
10+
11+
beforeEach(async () => {
12+
await TestBed.configureTestingModule({
13+
declarations: [DashboardDiseaseBurdenComponent],
14+
imports: [HttpClientTestingModule, MaterialModule],
15+
}).compileComponents();
16+
});
17+
18+
beforeEach(() => {
19+
fixture = TestBed.createComponent(DashboardDiseaseBurdenComponent);
20+
component = fixture.componentInstance;
21+
fixture.detectChanges();
22+
});
23+
24+
it('should create', () => {
25+
expect(component).toBeTruthy();
26+
});
27+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import { Component, OnDestroy, OnInit } from '@angular/core';
2+
import { Subscription } from 'rxjs';
3+
import { Filter, VIEW_OPTIONS } from '../../../_models/common';
4+
import { DiseaseBurdenService } from '../../../_services/api/disease-burden';
5+
import { FilterService } from '../../../_services/filter.service';
6+
import { NotificationService } from '../../../_services/notification.service';
7+
8+
@Component({
9+
selector: 'app-dashboard-disease-burden',
10+
templateUrl: './dashboard-disease-burden.component.html',
11+
styleUrls: ['./dashboard-disease-burden.component.scss'],
12+
})
13+
export class DashboardDiseaseBurdenComponent implements OnInit, OnDestroy {
14+
data: any;
15+
subscriptions: Subscription[] = [];
16+
filters: Filter[] = [];
17+
visible = true;
18+
viewOptions = VIEW_OPTIONS;
19+
20+
constructor(
21+
private diseaseBurdenService: DiseaseBurdenService,
22+
public filterService: FilterService,
23+
private notificationService: NotificationService
24+
) {}
25+
26+
ngOnInit(): void {
27+
this.subscriptions.push(
28+
this.filterService.getFilters().subscribe((response) => {
29+
this.filters = response.filters;
30+
this.fetchDiseaseBurden();
31+
})
32+
);
33+
}
34+
35+
fetchDiseaseBurden(): void {
36+
this.subscriptions.push(
37+
this.diseaseBurdenService.getCalculated(this.filters).subscribe({
38+
next: (data: any) => {
39+
this.data = data;
40+
},
41+
error: (err: any) => {
42+
this.notificationService.error(err);
43+
},
44+
complete: () => {},
45+
})
46+
);
47+
}
48+
49+
toggleSection(event: any): void {
50+
this.visible = !event.checked;
51+
}
52+
53+
ngOnDestroy(): void {
54+
this.subscriptions.forEach((subscription) => subscription.unsubscribe());
55+
}
56+
}

0 commit comments

Comments
 (0)