Skip to content

Commit

Permalink
Merge pull request #789 from catenax-ng/feature/TRACEFOSS-604-extend-…
Browse files Browse the repository at this point in the history
…dashboard

[CX-RELEASE-RELEVANT] Feature/tracefoss 604 extend dashboard
  • Loading branch information
ds-mwesener authored Nov 27, 2023
2 parents b44d977 + c17edf4 commit 06a3095
Show file tree
Hide file tree
Showing 45 changed files with 909 additions and 285 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,11 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
- DEPENDENCIES_FRONTEND, SECURITY.md, NOTICE.md, LICENSE file to frontend docker image
- Added a step-by-step guide to register a server in pgAdmin in the database dump README
- Documentation about technical users
- Added new dashboard layout and additional widgets

### Changed
- Fixed helm repository path for backend & frontend (wrong prefix)
- Refactored dashboard response
- Updated user manual
- Autocomplete endpoints changed owner String type param to Owner for input validation and sql injection prevention
- Autocomplete endpoints repository uses now criteria api rather than native query
Expand Down
18 changes: 15 additions & 3 deletions frontend/src/app/mocks/services/dashboard-mock/dashboard.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,19 @@
import { DashboardStatsResponse } from '@page/dashboard/model/dashboard.model';

export const mockDashboardStats: DashboardStatsResponse = {
otherParts: 5,
myParts: 3,
investigations: 20,
// notification counts (where open means notficaiton status not closed)
myPartsWithOpenAlerts: 82,
myPartsWithOpenInvestigations: 33543,
supplierPartsWithOpenAlerts: 3,
customerPartsWithOpenAlerts: 563,
supplierPartsWithOpenInvestigations: 4643,
customerPartsWithOpenInvestigations: 12,

// part counts
asBuiltCustomerParts: 100,
asPlannedCustomerParts: 50,
asBuiltSupplierParts: 163000,
asPlannedSupplierParts: 2563,
asBuiltOwnParts: 5300000,
asPlannedOwnParts: 11203
};
57 changes: 31 additions & 26 deletions frontend/src/app/modules/core/core.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,32 +19,37 @@
* SPDX-License-Identifier: Apache-2.0
********************************************************************************/

import { HTTP_INTERCEPTORS, HttpClientModule } from '@angular/common/http';
import { APP_INITIALIZER, NgModule } from '@angular/core';
import { MAT_DATE_LOCALE } from '@angular/material/core';
import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { environment } from '@env';
import { LayoutModule } from '@layout/layout.module';
import { AboutModule } from '@page/about/about.module';
import { AdminModule } from '@page/admin/admin.module';
import { DashboardModule } from '@page/dashboard/dashboard.module';
import { OtherPartsModule } from '@page/other-parts/other-parts.module';
import { PartsModule } from '@page/parts/parts.module';
import { ToastService } from '@shared/components/toasts/toast.service';
import { I18NextModule } from 'angular-i18next';
import { KeycloakAngularModule, KeycloakService } from 'keycloak-angular';
import { ApiInterceptor } from './api/api.interceptor';
import { ApiService } from './api/api.service';
import { HttpErrorInterceptor } from './api/http-error.interceptor';
import { AppComponent } from './app/app.component';
import { AuthService } from './auth/auth.service';
import { KeycloakHelper } from './auth/keycloak.helper';
import { MockedKeycloakService } from './auth/mocked-keycloak.service';
import { CoreRoutingModule } from './core.routing';
import { I18N_PROVIDERS } from './i18n/global-i18n.providers';
import { UserService } from './user/user.service';
import { ErrorPageModule } from '@page/error-page/error-page.module';
import {HTTP_INTERCEPTORS, HttpClientModule} from '@angular/common/http';
import {APP_INITIALIZER, NgModule} from '@angular/core';
import {MAT_DATE_LOCALE} from '@angular/material/core';
import {BrowserModule} from '@angular/platform-browser';
import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
import {environment} from '@env';
import {LayoutModule} from '@layout/layout.module';
import {AboutModule} from '@page/about/about.module';
import {AdminModule} from '@page/admin/admin.module';
import {DashboardModule} from '@page/dashboard/dashboard.module';
import {OtherPartsModule} from '@page/other-parts/other-parts.module';
import {PartsModule} from '@page/parts/parts.module';
import {ToastService} from '@shared/components/toasts/toast.service';
import {I18NextModule} from 'angular-i18next';
import {KeycloakAngularModule, KeycloakService} from 'keycloak-angular';
import {ApiInterceptor} from './api/api.interceptor';
import {ApiService} from './api/api.service';
import {HttpErrorInterceptor} from './api/http-error.interceptor';
import {AppComponent} from './app/app.component';
import {AuthService} from './auth/auth.service';
import {KeycloakHelper} from './auth/keycloak.helper';
import {MockedKeycloakService} from './auth/mocked-keycloak.service';
import {CoreRoutingModule} from './core.routing';
import {I18N_PROVIDERS} from './i18n/global-i18n.providers';
import {UserService} from './user/user.service';
import {ErrorPageModule} from '@page/error-page/error-page.module';
import {registerLocaleData} from "@angular/common";
import localeDe from '@angular/common/locales/de';
import localeDeExtra from '@angular/common/locales/extra/de';

registerLocaleData(localeDe, 'de', localeDeExtra)

@NgModule({
declarations: [AppComponent],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,76 +22,110 @@
import { Injectable } from '@angular/core';
import { Notifications } from '@shared/model/notification.model';
import { View } from '@shared/model/view.model';
import { AlertsService } from '@shared/service/alerts.service';
import { InvestigationsService } from '@shared/service/investigations.service';
import { PartsService } from '@shared/service/parts.service';
import { Observable, Subscription } from 'rxjs';
import { DashboardService } from '../core/dashboard.service';
import { DashboardState } from '../core/dashboard.state';
import { DashboardStats } from '../model/dashboard.model';

@Injectable()
export class DashboardFacade {
private assetNumbersSubscription: Subscription;
private investigationSubscription: Subscription;
private dashboardStatsSubscription: Subscription;
private investigationsReceivedSubscription: Subscription;
private investigationsCreatedSubscription: Subscription;
private alertsReceivedSubscription: Subscription;
private alertsCreatedSubscription: Subscription;


constructor(
private readonly dashboardService: DashboardService,
private readonly dashboardState: DashboardState,
private readonly partsService: PartsService,
private readonly investigationsService: InvestigationsService,
private readonly alertsService: AlertsService
) {}

public get numberOfMyParts$(): Observable<View<number>> {
return this.dashboardState.numberOfMyParts$;
public get dashboardStats$(): Observable<View<DashboardStats>> {
return this.dashboardState.dashboardStats$;
}

public get recentReceivedInvestigations$(): Observable<View<Notifications>> {
return this.dashboardState.recentReceivedInvestigations$;
}

public get numberOfOtherParts$(): Observable<View<number>> {
return this.dashboardState.numberOfOtherParts$;
public get recentCreatedInvestigations$(): Observable<View<Notifications>> {
return this.dashboardState.recentCreatedInvestigations$;
}

public get numberOfInvestigations$(): Observable<View<number>> {
return this.dashboardState.numberOfInvestigations$;
public get recentReceivedAlerts$(): Observable<View<Notifications>> {
return this.dashboardState.recentReceivedAlerts$;
}

public get investigations$(): Observable<View<Notifications>> {
return this.dashboardState.investigations$;
public get recentCreatedAlerts$(): Observable<View<Notifications>> {
return this.dashboardState.recentCreatedAlerts$;
}

public setDashboardData(): void {
this.setAssetNumbers();
this.setInvestigations();
this.setDashboardMetricData();
this.setReceivedInvestigations();
this.setCreatedInvestigations();
this.setReceivedAlerts();
this.setCreatedAlerts();
}

private setAssetNumbers(): void {
this.dashboardState.setNumberOfMyParts({ loader: true });
this.dashboardState.setNumberOfOtherParts({ loader: true });
this.dashboardState.setNumberOfInvestigations({ loader: true });
private setDashboardMetricData(): void {
this.dashboardState.setDashboardStats({loader: true})

this.assetNumbersSubscription?.unsubscribe();
this.assetNumbersSubscription = this.dashboardService.getStats().subscribe({
this.dashboardStatsSubscription?.unsubscribe();
this.dashboardStatsSubscription = this.dashboardService.getStats().subscribe({
next: (dashboardStats: DashboardStats) => {
this.dashboardState.setNumberOfMyParts({ data: dashboardStats.myItems });
this.dashboardState.setNumberOfOtherParts({ data: dashboardStats.otherParts });
this.dashboardState.setNumberOfInvestigations({ data: dashboardStats.investigations || 0 });
this.dashboardState.setDashboardStats({data: dashboardStats})

},
error: error => {
this.dashboardState.setNumberOfMyParts({ error });
this.dashboardState.setNumberOfOtherParts({ error });
this.dashboardState.setNumberOfInvestigations({ error });
this.dashboardState.setDashboardStats({error})
},
});
}

public stopDataLoading(): void {
this.assetNumbersSubscription?.unsubscribe();
this.investigationSubscription?.unsubscribe();
this.dashboardStatsSubscription?.unsubscribe();
this.investigationsReceivedSubscription?.unsubscribe();
this.investigationsCreatedSubscription?.unsubscribe();
this.alertsReceivedSubscription?.unsubscribe();
this.alertsCreatedSubscription?.unsubscribe();
}

private setReceivedInvestigations(): void {
this.investigationsReceivedSubscription?.unsubscribe();
this.investigationsReceivedSubscription = this.investigationsService.getReceivedInvestigations(0, 5, [['createdDate','desc']]).subscribe({
next: data => this.dashboardState.setRecentReceivedInvestigations({ data }),
error: (error: Error) => this.dashboardState.setRecentReceivedInvestigations({ error }),
});
}

private setInvestigations(): void {
this.investigationSubscription?.unsubscribe();
this.investigationSubscription = this.investigationsService.getReceivedInvestigations(0, 5, []).subscribe({
next: data => this.dashboardState.setInvestigation({ data }),
error: (error: Error) => this.dashboardState.setInvestigation({ error }),
private setCreatedInvestigations(): void {
this.investigationsCreatedSubscription?.unsubscribe();
this.investigationsCreatedSubscription = this.investigationsService.getCreatedInvestigations(0, 5, [['createdDate','desc']]).subscribe({
next: data => this.dashboardState.setRecentCreatedInvestigations({ data }),
error: (error: Error) => this.dashboardState.setRecentCreatedInvestigations({ error }),
});
}

private setReceivedAlerts(): void {
this.alertsReceivedSubscription?.unsubscribe();
this.alertsReceivedSubscription = this.alertsService.getReceivedAlerts(0, 5, [['createdDate','desc']]).subscribe({
next: data => this.dashboardState.setRecentReceivedAlerts({data}),
error: (error: Error) => this.dashboardState.setRecentReceivedAlerts({ error }),
});
}


private setCreatedAlerts(): void {
this.alertsCreatedSubscription?.unsubscribe();
this.alertsCreatedSubscription = this.alertsService.getCreatedAlerts(0, 5, [['createdDate','desc']]).subscribe({
next: data => this.dashboardState.setRecentCreatedAlerts({data}),
error: (error: Error) => this.dashboardState.setRecentCreatedAlerts({ error }),
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,38 @@ describe('DashboardAssembler', () => {
it('should map response', () => {
expect(
DashboardAssembler.assembleDashboard({
otherParts: 100,
myParts: 200,
asBuiltOwnParts: 200,
asBuiltCustomerParts: 0,
asBuiltSupplierParts: 10,
asPlannedCustomerParts: 0,
asPlannedOwnParts: 0,
asPlannedSupplierParts: 0,
customerPartsWithOpenAlerts: 0,
customerPartsWithOpenInvestigations: 0,
myPartsWithOpenAlerts: 0,
myPartsWithOpenInvestigations: 0,
supplierPartsWithOpenAlerts: 0,
supplierPartsWithOpenInvestigations: 0,

}),
).toEqual({
otherParts: 100,
myItems: 200,
investigations: undefined,
asBuiltCustomerParts: 0,
asBuiltSupplierParts: 10,
asPlannedCustomerParts: 0,
asPlannedOwnParts: 0,
asPlannedSupplierParts: 0,
asBuiltOwnParts: 200,

totalOwnParts: 200,
totalOtherParts: 10,

ownOpenInvestigationsReceived: 0,
ownOpenInvestigationsCreated: 0,
ownOpenAlertsReceived: 0,
ownOpenAlertsCreated: 0,

myPartsWithOpenAlerts: 0,
myPartsWithOpenInvestigations: 0
});
});
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,26 @@ import { DashboardStats, DashboardStatsResponse } from '../model/dashboard.model
export class DashboardAssembler {
public static assembleDashboard(dashboard: DashboardStatsResponse): DashboardStats {
return {
otherParts: dashboard.otherParts,
myItems: dashboard.myParts,
investigations: dashboard.investigations,
// notification counts (where open means notfication status not closed)
myPartsWithOpenAlerts: dashboard.myPartsWithOpenAlerts,
myPartsWithOpenInvestigations: dashboard.myPartsWithOpenInvestigations,

// part counts
asBuiltCustomerParts: dashboard.asBuiltCustomerParts,
asPlannedCustomerParts: dashboard.asPlannedCustomerParts,
asBuiltSupplierParts: dashboard.asBuiltSupplierParts,
asPlannedSupplierParts: dashboard.asPlannedSupplierParts,
asBuiltOwnParts: dashboard.asBuiltOwnParts,
asPlannedOwnParts: dashboard.asPlannedOwnParts,

// calculated
totalOwnParts: dashboard.asBuiltOwnParts + dashboard.asPlannedOwnParts,
totalOtherParts: dashboard.asBuiltSupplierParts + dashboard.asBuiltCustomerParts + dashboard.asPlannedSupplierParts + dashboard.asPlannedSupplierParts,
ownOpenInvestigationsReceived: dashboard.myPartsWithOpenInvestigations,
ownOpenInvestigationsCreated: dashboard.supplierPartsWithOpenInvestigations + dashboard.customerPartsWithOpenInvestigations,
ownOpenAlertsReceived: dashboard.supplierPartsWithOpenAlerts + dashboard.customerPartsWithOpenAlerts,
ownOpenAlertsCreated: dashboard.myPartsWithOpenAlerts,

};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,13 @@
* SPDX-License-Identifier: Apache-2.0
********************************************************************************/

import { Injectable } from '@angular/core';
import { ApiService } from '@core/api/api.service';
import { environment } from '@env';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { DashboardStats, DashboardStatsResponse } from '../model/dashboard.model';
import { DashboardAssembler } from './dashboard.assembler';
import {Injectable} from '@angular/core';
import {ApiService} from '@core/api/api.service';
import {environment} from '@env';
import {Observable} from 'rxjs';
import {map} from 'rxjs/operators';
import {DashboardStats, DashboardStatsResponse} from '../model/dashboard.model';
import {DashboardAssembler} from './dashboard.assembler';

@Injectable()
export class DashboardService {
Expand Down
Loading

0 comments on commit 06a3095

Please sign in to comment.