From 45fc800260785a5a3baebdd6565d38dc782342c2 Mon Sep 17 00:00:00 2001 From: Martin Maul Date: Mon, 20 Nov 2023 17:33:33 +0100 Subject: [PATCH 01/17] feature(UI):[TRACEFOSS-604] extended dashboard --- .../dashboard-mock/dashboard.model.ts | 15 +- .../dashboard/abstraction/dashboard.facade.ts | 88 ++++++++--- .../dashboard/core/dashboard.assembler.ts | 17 +- .../page/dashboard/core/dashboard.service.ts | 2 +- .../page/dashboard/core/dashboard.state.ts | 145 +++++++++++++++--- .../page/dashboard/model/dashboard.model.ts | 34 +++- .../presentation/dashboard.component.html | 117 ++++++++++---- .../presentation/dashboard.component.scss | 4 + .../presentation/dashboard.component.ts | 36 +++-- 9 files changed, 361 insertions(+), 97 deletions(-) diff --git a/frontend/src/app/mocks/services/dashboard-mock/dashboard.model.ts b/frontend/src/app/mocks/services/dashboard-mock/dashboard.model.ts index fec65bd19f..82f60d1723 100644 --- a/frontend/src/app/mocks/services/dashboard-mock/dashboard.model.ts +++ b/frontend/src/app/mocks/services/dashboard-mock/dashboard.model.ts @@ -22,7 +22,16 @@ 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: 10, + myPartsWithOpenInvestigations: 8, + otherPartsWithOpenAlerts: 3, + otherPartsWithOpenInvestigations: 5, + // part counts + asBuiltCustomerParts: 50, + asPlannedCustomerParts: 5, + asBuiltSupplierParts: 500, + asPlannedSupplierParts: 5000, + asBuiltOwnParts: 10000, + asPlannedOwnParts: 555555 }; diff --git a/frontend/src/app/modules/page/dashboard/abstraction/dashboard.facade.ts b/frontend/src/app/modules/page/dashboard/abstraction/dashboard.facade.ts index 35638ba010..7d48fb69b3 100644 --- a/frontend/src/app/modules/page/dashboard/abstraction/dashboard.facade.ts +++ b/frontend/src/app/modules/page/dashboard/abstraction/dashboard.facade.ts @@ -22,8 +22,8 @@ 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'; @@ -33,51 +33,89 @@ import { DashboardStats } from '../model/dashboard.model'; export class DashboardFacade { private assetNumbersSubscription: Subscription; private investigationSubscription: Subscription; + private alertsSubscription: Subscription; constructor( private readonly dashboardService: DashboardService, private readonly dashboardState: DashboardState, - private readonly partsService: PartsService, + // private readonly partsService: PartsService, private readonly investigationsService: InvestigationsService, + private readonly alertsService: AlertsService ) {} public get numberOfMyParts$(): Observable> { - return this.dashboardState.numberOfMyParts$; + return this.dashboardState.numberOfTotalMyParts$; } - +/* public get numberOfOtherParts$(): Observable> { - return this.dashboardState.numberOfOtherParts$; + return this.dashboardState.numberOfAsBuiltSupplierParts$.subscribe(next => next.data as number) + } + + */ + public get numberOfMyPartsWithOpenInvestigations$(): Observable> { + return this.dashboardState.numberOfMyPartsWithOpenInvestigations$; } - public get numberOfInvestigations$(): Observable> { - return this.dashboardState.numberOfInvestigations$; + public get recentInvestigations$(): Observable> { + return this.dashboardState.recentInvestigations$; } - public get investigations$(): Observable> { - return this.dashboardState.investigations$; + public get recentAlerts$(): Observable> { + return this.dashboardState.recentAlerts$; } public setDashboardData(): void { this.setAssetNumbers(); this.setInvestigations(); + this.setAlerts(); } private setAssetNumbers(): void { - this.dashboardState.setNumberOfMyParts({ loader: true }); - this.dashboardState.setNumberOfOtherParts({ loader: true }); - this.dashboardState.setNumberOfInvestigations({ loader: true }); + this.dashboardState.setNumberOfTotalMyParts({ loader: true }); + + this.dashboardState.setNumberOfAsBuiltOwnParts({loader: true}); + this.dashboardState.setNumberOfAsPlannedOwnParts({loader: true}); + this.dashboardState.setNumberOfAsBuiltSupplierParts({loader: true}); + this.dashboardState.setNumberOfAsPlannedSupplierParts({loader: true}); + this.dashboardState.setNumberOfAsBuiltCustomerParts({loader: true}); + this.dashboardState.setNumberOfAsPlannedCustomerParts({loader: true}); + + this.dashboardState.setNumberOfMyPartsWithOpenInvestigations({loader: true}); + this.dashboardState.setNumberOfMyPartsWithOpenAlerts({loader:true}); + this.dashboardState.setNumberOfOtherPartsWithOpenInvestigations({loader:true}); + this.dashboardState.setNumberOfOtherPartsWithOpenAlerts({loader:true}); this.assetNumbersSubscription?.unsubscribe(); this.assetNumbersSubscription = 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.setNumberOfTotalMyParts({ data: dashboardStats.totalOwnParts }); + + this.dashboardState.setNumberOfAsBuiltOwnParts({data: dashboardStats.asBuiltOwnParts}); + this.dashboardState.setNumberOfAsPlannedOwnParts({data: dashboardStats.asPlannedOwnParts}); + this.dashboardState.setNumberOfAsBuiltSupplierParts({data: dashboardStats.asBuiltSupplierParts}); + this.dashboardState.setNumberOfAsPlannedSupplierParts({data: dashboardStats.asPlannedSupplierParts}); + this.dashboardState.setNumberOfAsBuiltCustomerParts({data: dashboardStats.asBuiltCustomerParts}); + this.dashboardState.setNumberOfAsPlannedCustomerParts({data: dashboardStats.asPlannedCustomerParts}); + + this.dashboardState.setNumberOfMyPartsWithOpenInvestigations({data: dashboardStats.myPartsWithOpenInvestigations}); + this.dashboardState.setNumberOfMyPartsWithOpenAlerts({data: dashboardStats.myPartsWithOpenAlerts}); + this.dashboardState.setNumberOfOtherPartsWithOpenInvestigations({data: dashboardStats.otherPartsWithOpenInvestigations}); + this.dashboardState.setNumberOfOtherPartsWithOpenAlerts({data: dashboardStats.otherPartsWithOpenAlerts}); }, error: error => { - this.dashboardState.setNumberOfMyParts({ error }); - this.dashboardState.setNumberOfOtherParts({ error }); - this.dashboardState.setNumberOfInvestigations({ error }); + this.dashboardState.setNumberOfTotalMyParts({ error }); + + this.dashboardState.setNumberOfAsBuiltOwnParts({error}); + this.dashboardState.setNumberOfAsPlannedOwnParts({error}); + this.dashboardState.setNumberOfAsBuiltSupplierParts({error}); + this.dashboardState.setNumberOfAsPlannedSupplierParts({error}); + this.dashboardState.setNumberOfAsBuiltCustomerParts({error}); + this.dashboardState.setNumberOfAsPlannedCustomerParts({error}); + + this.dashboardState.setNumberOfMyPartsWithOpenInvestigations({error}); + this.dashboardState.setNumberOfMyPartsWithOpenAlerts({error}); + this.dashboardState.setNumberOfOtherPartsWithOpenInvestigations({error}); + this.dashboardState.setNumberOfOtherPartsWithOpenAlerts({error}); }, }); } @@ -89,9 +127,17 @@ export class DashboardFacade { 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 }), + this.investigationSubscription = this.investigationsService.getReceivedInvestigations(0, 5, [['createdDate','desc']]).subscribe({ + next: data => this.dashboardState.setInvestigationsReceived({ data }), + error: (error: Error) => this.dashboardState.setInvestigationsReceived({ error }), + }); + } + + private setAlerts(): void { + this.alertsSubscription?.unsubscribe(); + this.alertsSubscription = this.alertsService.getReceivedAlerts(0, 5, [['createdDate','desc']]).subscribe({ + next: data => this.dashboardState.setRecentAlerts({data}), + error: (error: Error) => this.dashboardState.setRecentAlerts({ error }), }); } } diff --git a/frontend/src/app/modules/page/dashboard/core/dashboard.assembler.ts b/frontend/src/app/modules/page/dashboard/core/dashboard.assembler.ts index fbf4b171b2..16ec8e063b 100644 --- a/frontend/src/app/modules/page/dashboard/core/dashboard.assembler.ts +++ b/frontend/src/app/modules/page/dashboard/core/dashboard.assembler.ts @@ -24,9 +24,20 @@ 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 notficaiton status not closed) + myPartsWithOpenAlerts: dashboard.myPartsWithOpenAlerts, + myPartsWithOpenInvestigations: dashboard.myPartsWithOpenInvestigations, + otherPartsWithOpenAlerts: dashboard.otherPartsWithOpenAlerts, + otherPartsWithOpenInvestigations: dashboard.otherPartsWithOpenInvestigations, + // part counts + asBuiltCustomerParts: dashboard.asBuiltCustomerParts, + asPlannedCustomerParts: dashboard.asPlannedCustomerParts, + asBuiltSupplierParts: dashboard.asBuiltSupplierParts, + asPlannedSupplierParts: dashboard.asPlannedSupplierParts, + asBuiltOwnParts: dashboard.asBuiltOwnParts, + asPlannedOwnParts: dashboard.asPlannedOwnParts, + totalOwnParts: dashboard.asBuiltOwnParts + dashboard.asPlannedOwnParts, + totalOtherParts: dashboard.asBuiltSupplierParts + dashboard.asBuiltCustomerParts, }; } } diff --git a/frontend/src/app/modules/page/dashboard/core/dashboard.service.ts b/frontend/src/app/modules/page/dashboard/core/dashboard.service.ts index e3aaa52fb6..647da41772 100644 --- a/frontend/src/app/modules/page/dashboard/core/dashboard.service.ts +++ b/frontend/src/app/modules/page/dashboard/core/dashboard.service.ts @@ -36,6 +36,6 @@ export class DashboardService { public getStats(): Observable { return this.apiService .get(`${this.url}/dashboard`) - .pipe(map((payload: DashboardStatsResponse) => DashboardAssembler.assembleDashboard(payload))); + .pipe(map((payload: DashboardStatsResponse) => DashboardAssembler.assembleDashboard(payload))) } } diff --git a/frontend/src/app/modules/page/dashboard/core/dashboard.state.ts b/frontend/src/app/modules/page/dashboard/core/dashboard.state.ts index a1a93f6045..676dec0577 100644 --- a/frontend/src/app/modules/page/dashboard/core/dashboard.state.ts +++ b/frontend/src/app/modules/page/dashboard/core/dashboard.state.ts @@ -28,41 +28,146 @@ import { View } from 'src/app/modules/shared/model/view.model'; @Injectable() export class DashboardState { - private readonly _numberOfMyParts$: State> = new State>({ loader: true }); - private readonly _numberOfOtherParts$: State> = new State>({ loader: true }); - private readonly _numberOfInvestigations$: State> = new State>({ loader: true }); - private readonly _investigations$: State> = new State>({ loader: true }); + // part counts + private readonly _numberOfAsBuiltOwnParts$: State> = new State>({ loader: true }); + private readonly _numberOfAsPlannedOwnParts$: State> = new State>({ loader: true }); + private readonly _numberOfAsBuiltSupplierParts$: State> = new State>({ loader: true }); + private readonly _numberOfAsPlannedSupplierParts$: State> = new State>({ loader: true }); + private readonly _numberOfAsBuiltCustomerParts$: State> = new State>({ loader: true }); + private readonly _numberOfAsPlannedCustomerParts$: State> = new State>({ loader: true }); - public get numberOfMyParts$(): Observable> { - return this._numberOfMyParts$.observable; + // calculated part counts + private readonly _numberOfTotalMyParts$: State> = new State>({ loader: true }); + private readonly _numberOfTotalOtherParts$: State> = new State>({ loader: true }); + + // notification counts + private readonly _numberOfMyPartsWithOpenAlerts$: State> = new State>({ loader: true }); + private readonly _numberOfMyPartsWithOpenInvestigations$: State> = new State>({ loader: true }); + private readonly _numberOfOtherPartsWithOpenAlerts$: State> = new State>({ loader: true }); + private readonly _numberOfOtherPartsWithOpenInvestigations$: State> = new State>({ loader: true }); + + + // notifications received + private readonly _recentInvestigations$: State> = new State>({ loader: true }); + private readonly _recentAlerts$: State> = new State>({ loader: true }); + + /** + * part counts getter/setter + */ + + public get numberOfAsBuiltOwnParts$(): Observable> { + return this._numberOfAsBuiltOwnParts$.observable; + } + + public setNumberOfAsBuiltOwnParts(count: View): void { + this._numberOfAsBuiltOwnParts$.update(count); + } + + public get numberOfAsPlannedOwnParts$(): Observable> { + return this._numberOfAsPlannedOwnParts$.observable; + } + + public setNumberOfAsPlannedOwnParts(count: View): void { + this._numberOfAsPlannedOwnParts$.update(count); + } + + public get numberOfAsBuiltSupplierParts$(): Observable> { + return this._numberOfAsBuiltSupplierParts$.observable; + } + + public setNumberOfAsBuiltSupplierParts(count: View): void { + this._numberOfAsBuiltSupplierParts$.update(count); + } + + public get numberOfAsPlannedSupplierParts$(): Observable> { + return this._numberOfAsPlannedSupplierParts$.observable; + } + + public setNumberOfAsPlannedSupplierParts(count: View): void { + this._numberOfAsPlannedSupplierParts$.update(count); + } + + public get numberOfAsBuiltCustomerParts$(): Observable> { + return this._numberOfAsBuiltCustomerParts$.observable; + } + + public setNumberOfAsBuiltCustomerParts(count: View): void { + this._numberOfAsBuiltCustomerParts$.update(count); } - public setNumberOfMyParts(count: View): void { - this._numberOfMyParts$.update(count); + public get numberOfAsPlannedCustomerParts$(): Observable> { + return this._numberOfAsPlannedCustomerParts$.observable; } - public get numberOfOtherParts$(): Observable> { - return this._numberOfOtherParts$.observable; + public setNumberOfAsPlannedCustomerParts(count: View): void { + this._numberOfAsPlannedCustomerParts$.update(count); } - public setNumberOfOtherParts(count: View): void { - this._numberOfOtherParts$.update(count); + public get numberOfTotalMyParts$(): Observable> { + return this._numberOfTotalMyParts$.observable; } - public get numberOfInvestigations$(): Observable> { - return this._numberOfInvestigations$.observable; + public setNumberOfTotalMyParts(count: View): void { + this._numberOfTotalMyParts$.update(count); + } + + + /** + * part notifications getter/setter + */ + + + public get numberOfMyPartsWithOpenAlerts$(): Observable> { + return this._numberOfMyPartsWithOpenAlerts$.observable; + } + + public setNumberOfMyPartsWithOpenAlerts(count: View): void { + this._numberOfMyPartsWithOpenAlerts$.update(count); + } + + public get numberOfMyPartsWithOpenInvestigations$(): Observable> { + return this._numberOfMyPartsWithOpenInvestigations$.observable; + } + + public setNumberOfMyPartsWithOpenInvestigations(count: View): void { + this._numberOfMyPartsWithOpenInvestigations$.update(count); + } + + public get numberOfOtherPartsWithOpenAlerts$(): Observable> { + return this._numberOfOtherPartsWithOpenAlerts$.observable; + } + + public setNumberOfOtherPartsWithOpenAlerts(count: View): void { + this._numberOfOtherPartsWithOpenAlerts$.update(count); + } + + public get numberOfOtherPartsWithOpenInvestigations$(): Observable> { + return this._numberOfOtherPartsWithOpenInvestigations$.observable; + } + + public setNumberOfOtherPartsWithOpenInvestigations(count: View): void { + this._numberOfOtherPartsWithOpenInvestigations$.update(count); + } + + /** + * recent notifications getter/setter + */ + + + public get recentInvestigations$(): Observable> { + return this._recentInvestigations$.observable; } - public setNumberOfInvestigations(count: View): void { - this._numberOfInvestigations$.update(count); + public setInvestigationsReceived(investigations: View): void { + this._recentInvestigations$.update(investigations); } - public get investigations$(): Observable> { - return this._investigations$.observable; + public get recentAlerts$(): Observable> { + return this._recentAlerts$.observable; } - public setInvestigation(investigations: View): void { - this._investigations$.update(investigations); + public setRecentAlerts(alerts: View): void { + this._recentAlerts$.update(alerts); } } diff --git a/frontend/src/app/modules/page/dashboard/model/dashboard.model.ts b/frontend/src/app/modules/page/dashboard/model/dashboard.model.ts index 6a9b5e02eb..96695194b7 100644 --- a/frontend/src/app/modules/page/dashboard/model/dashboard.model.ts +++ b/frontend/src/app/modules/page/dashboard/model/dashboard.model.ts @@ -20,13 +20,35 @@ ********************************************************************************/ export interface DashboardStats { - otherParts: number | null; - myItems: number; - investigations?: number; + // notification counts (where open means notficaiton status not closed) + myPartsWithOpenAlerts: number, + myPartsWithOpenInvestigations: number, + otherPartsWithOpenAlerts: number, + otherPartsWithOpenInvestigations: number, + // part counts + asBuiltCustomerParts: number, + asPlannedCustomerParts: number, + asBuiltSupplierParts: number, + asPlannedSupplierParts: number, + asBuiltOwnParts: number, + asPlannedOwnParts: number + + // calculated counts + totalOwnParts: number + totalOtherParts: number } export interface DashboardStatsResponse { - otherParts: number | null; - myParts: number; - investigations?: number; + // notification counts (where open means notficaiton status not closed) + myPartsWithOpenAlerts: number, + myPartsWithOpenInvestigations: number, + otherPartsWithOpenAlerts: number, + otherPartsWithOpenInvestigations: number, + // part counts + asBuiltCustomerParts: number, + asPlannedCustomerParts: number, + asBuiltSupplierParts: number, + asPlannedSupplierParts: number, + asBuiltOwnParts: number, + asPlannedOwnParts: number } diff --git a/frontend/src/app/modules/page/dashboard/presentation/dashboard.component.html b/frontend/src/app/modules/page/dashboard/presentation/dashboard.component.html index 91410d2640..16583c1d9a 100644 --- a/frontend/src/app/modules/page/dashboard/presentation/dashboard.component.html +++ b/frontend/src/app/modules/page/dashboard/presentation/dashboard.component.html @@ -59,42 +59,91 @@
- - -
-
-

- find_in_page - {{ 'pageTitle.investigations' | i18n }} -

- -
- remove_red_eye - {{ 'commonInvestigation.viewAll' | i18n }} -
-
+ + + + + +
+
+

+ find_in_page + {{ 'pageTitle.investigations' | i18n }} +

+ +
+ remove_red_eye + {{ 'commonInvestigation.viewAll' | i18n }} +
+
+
-
- - - - - - + + + + + + + + +
+ +
+ + + + + +
+
+

+ find_in_page + {{ 'pageTitle.alerts' | i18n }} +

+ +
+ remove_red_eye + {{ 'commonInvestigation.viewAll' | i18n }} +
+
+
+
+
+ + + + +
+
+
+ diff --git a/frontend/src/app/modules/page/dashboard/presentation/dashboard.component.scss b/frontend/src/app/modules/page/dashboard/presentation/dashboard.component.scss index 25aab57ab8..7c4b0cd6c3 100644 --- a/frontend/src/app/modules/page/dashboard/presentation/dashboard.component.scss +++ b/frontend/src/app/modules/page/dashboard/presentation/dashboard.component.scss @@ -30,3 +30,7 @@ .dashboard--investigation { @apply col-span-12; } + +.dashboard--alert { + @apply col-span-12; +} diff --git a/frontend/src/app/modules/page/dashboard/presentation/dashboard.component.ts b/frontend/src/app/modules/page/dashboard/presentation/dashboard.component.ts index 638ad35082..317183634f 100644 --- a/frontend/src/app/modules/page/dashboard/presentation/dashboard.component.ts +++ b/frontend/src/app/modules/page/dashboard/presentation/dashboard.component.ts @@ -21,8 +21,13 @@ import { Component, OnDestroy, OnInit, ViewChild } from '@angular/core'; import { Router } from '@angular/router'; -import { getRoute, INVESTIGATION_BASE_ROUTE } from '@core/known-route'; -import {Notification, Notifications, NotificationStatusGroup, NotificationType} from '@shared/model/notification.model'; +import { ALERT_BASE_ROUTE, getRoute, INVESTIGATION_BASE_ROUTE } from '@core/known-route'; +import { + Notification, + Notifications, + NotificationStatusGroup, + NotificationType, +} from '@shared/model/notification.model'; import { View } from '@shared/model/view.model'; import { CloseNotificationModalComponent } from '@shared/modules/notification/modal/close/close-notification-modal.component'; import { Observable } from 'rxjs'; @@ -41,20 +46,28 @@ export class DashboardComponent implements OnInit, OnDestroy { public readonly numberOfInvestigations$: Observable>; public readonly investigations$: Observable>; + public readonly alerts$: Observable>; public readonly investigationLink: string; public readonly investigationParams: Record; + public readonly alertLink: string; + public readonly alertParams: Record; + constructor(private readonly dashboardFacade: DashboardFacade, private readonly router: Router) { this.numberOfMyParts$ = this.dashboardFacade.numberOfMyParts$; - this.numberOfOtherParts$ = this.dashboardFacade.numberOfOtherParts$; - this.numberOfInvestigations$ = this.dashboardFacade.numberOfInvestigations$; + //this.numberOfOtherParts$ = this.dashboardFacade.numberOfOtherParts$; + this.numberOfInvestigations$ = this.dashboardFacade.numberOfMyPartsWithOpenInvestigations$; - this.investigations$ = this.dashboardFacade.investigations$; + this.investigations$ = this.dashboardFacade.recentInvestigations$; + this.alerts$ = this.dashboardFacade.recentAlerts$ - const { link, queryParams } = getRoute(INVESTIGATION_BASE_ROUTE, NotificationStatusGroup.RECEIVED); - this.investigationLink = link; - this.investigationParams = queryParams; + const { link: investigationLink, queryParams: investigationQueryParams } = getRoute(INVESTIGATION_BASE_ROUTE, NotificationStatusGroup.RECEIVED); + const { link: alertLink, queryParams: alertQueryParams } = getRoute(ALERT_BASE_ROUTE, NotificationStatusGroup.RECEIVED); + this.investigationLink = investigationLink; + this.investigationParams = investigationQueryParams; + this.alertLink = alertLink; + this.alertParams = alertQueryParams; } public ngOnInit(): void { @@ -65,10 +78,15 @@ export class DashboardComponent implements OnInit, OnDestroy { this.dashboardFacade.stopDataLoading(); } - public onNotificationSelected(notification: Notification): void { + public onInvestigationSelected(notification: Notification): void { const { link } = getRoute(INVESTIGATION_BASE_ROUTE); this.router.navigate([`/${link}/${notification.id}`]).then(); } + public onAlertSelected(notification: Notification): void { + const { link } = getRoute(ALERT_BASE_ROUTE); + this.router.navigate([`/${link}/${notification.id}`]).then(); + } + protected readonly NotificationType = NotificationType; } From 6509c2b4c2972218970e555aa0ad0e00bb6edeec Mon Sep 17 00:00:00 2001 From: ds-lcapellino Date: Tue, 21 Nov 2023 09:47:43 +0100 Subject: [PATCH 02/17] feature: TRACEFOSS-604 refactor dashboard response --- .../mapper/DashboardResponseMapper.java | 15 +++--- .../assets/domain/base/AssetRepository.java | 3 ++ .../domain/dashboard/model/Dashboard.java | 17 ++++--- .../service/DashboardServiceImpl.java | 41 +++++++++------- .../AssetAsBuiltRepositoryImpl.java | 5 ++ .../repository/JpaAssetAsBuiltRepository.java | 4 +- .../AssetAsPlannedRepositoryImpl.java | 6 +++ .../JpaAssetAsPlannedRepository.java | 5 +- .../base/model/QualityNotificationStatus.java | 2 +- .../QualityNotificationRepository.java | 4 ++ .../repository/AlertsRepositoryImpl.java | 13 +++++ .../InvestigationsRepositoryImpl.java | 13 +++++ .../JpaInvestigationRepository.java | 3 ++ .../rest/DashboardControllerTest.java | 34 +++++++------ .../assets/DashboardControllerIT.java | 49 ++++++++++++------- .../assets/response/DashboardResponse.java | 18 ++++--- .../asbuilt/AssetAsBuiltResponse.java | 11 +++++ 17 files changed, 167 insertions(+), 76 deletions(-) diff --git a/tx-backend/src/main/java/org/eclipse/tractusx/traceability/assets/application/dashboard/mapper/DashboardResponseMapper.java b/tx-backend/src/main/java/org/eclipse/tractusx/traceability/assets/application/dashboard/mapper/DashboardResponseMapper.java index 9d7fc2616e..56f9a65c9a 100644 --- a/tx-backend/src/main/java/org/eclipse/tractusx/traceability/assets/application/dashboard/mapper/DashboardResponseMapper.java +++ b/tx-backend/src/main/java/org/eclipse/tractusx/traceability/assets/application/dashboard/mapper/DashboardResponseMapper.java @@ -25,13 +25,16 @@ public class DashboardResponseMapper { public static DashboardResponse from(final Dashboard dashboard) { return new DashboardResponse( - dashboard.getMyParts(), - dashboard.getOtherParts(), - dashboard.getInvestigationsReceived(), - dashboard.getAlertsReceived(), - dashboard.getAlertsSent(), + dashboard.getAsBuiltCustomerParts(), + dashboard.getAsPlannedCustomerParts(), + dashboard.getAsBuiltSupplierParts(), + dashboard.getAsPlannedSupplierParts(), + dashboard.getAsBuiltOwnParts(), + dashboard.getAsPlannedOwnParts(), dashboard.getMyPartsWithOpenAlerts(), - dashboard.getSupplierPartsWithOpenAlerts() + dashboard.getMyPartsWithOpenInvestigations(), + dashboard.getOtherPartsWithOpenAlerts(), + dashboard.getOtherPartsWithOpenInvestigations() ); } } diff --git a/tx-backend/src/main/java/org/eclipse/tractusx/traceability/assets/domain/base/AssetRepository.java b/tx-backend/src/main/java/org/eclipse/tractusx/traceability/assets/domain/base/AssetRepository.java index 6501fe9491..b19584dba2 100644 --- a/tx-backend/src/main/java/org/eclipse/tractusx/traceability/assets/domain/base/AssetRepository.java +++ b/tx-backend/src/main/java/org/eclipse/tractusx/traceability/assets/domain/base/AssetRepository.java @@ -21,6 +21,7 @@ import org.eclipse.tractusx.traceability.assets.domain.base.model.AssetBase; import org.eclipse.tractusx.traceability.assets.domain.base.model.Owner; +import org.eclipse.tractusx.traceability.assets.infrastructure.base.model.AssetBaseEntity; import org.eclipse.tractusx.traceability.common.model.PageResult; import org.eclipse.tractusx.traceability.common.model.SearchCriteria; import org.springframework.data.domain.Pageable; @@ -50,5 +51,7 @@ public interface AssetRepository { long countAssetsByOwner(Owner owner); + List findByOwner(Owner owner); + List getFieldValues(String fieldName, String startWith, Long resultLimit, String owner); } diff --git a/tx-backend/src/main/java/org/eclipse/tractusx/traceability/assets/domain/dashboard/model/Dashboard.java b/tx-backend/src/main/java/org/eclipse/tractusx/traceability/assets/domain/dashboard/model/Dashboard.java index ba22b4b443..479a6c7cfe 100644 --- a/tx-backend/src/main/java/org/eclipse/tractusx/traceability/assets/domain/dashboard/model/Dashboard.java +++ b/tx-backend/src/main/java/org/eclipse/tractusx/traceability/assets/domain/dashboard/model/Dashboard.java @@ -27,11 +27,14 @@ @Data @Builder public class Dashboard { - Long myParts; - Long otherParts; - Long investigationsReceived; - Long alertsReceived; - Long alertsSent; - Long myPartsWithOpenAlerts; - Long supplierPartsWithOpenAlerts; + long asBuiltCustomerParts; + long asPlannedCustomerParts; + long asBuiltSupplierParts; + long asPlannedSupplierParts; + long asBuiltOwnParts; + long asPlannedOwnParts; + long myPartsWithOpenAlerts; + long myPartsWithOpenInvestigations; + long otherPartsWithOpenAlerts; + long otherPartsWithOpenInvestigations; } diff --git a/tx-backend/src/main/java/org/eclipse/tractusx/traceability/assets/domain/dashboard/service/DashboardServiceImpl.java b/tx-backend/src/main/java/org/eclipse/tractusx/traceability/assets/domain/dashboard/service/DashboardServiceImpl.java index 62634a4efb..ebc33946ed 100644 --- a/tx-backend/src/main/java/org/eclipse/tractusx/traceability/assets/domain/dashboard/service/DashboardServiceImpl.java +++ b/tx-backend/src/main/java/org/eclipse/tractusx/traceability/assets/domain/dashboard/service/DashboardServiceImpl.java @@ -29,7 +29,6 @@ import org.eclipse.tractusx.traceability.assets.domain.dashboard.model.Dashboard; import org.eclipse.tractusx.traceability.qualitynotification.domain.base.AlertRepository; import org.eclipse.tractusx.traceability.qualitynotification.domain.base.InvestigationRepository; -import org.eclipse.tractusx.traceability.qualitynotification.domain.base.model.QualityNotificationStatus; import org.springframework.stereotype.Component; import java.util.List; @@ -45,24 +44,32 @@ public class DashboardServiceImpl implements DashboardService { @Override public Dashboard getDashboard() { - long customerParts = assetAsBuiltRepository.countAssetsByOwner(Owner.CUSTOMER) + assetAsPlannedRepository.countAssetsByOwner(Owner.CUSTOMER); - long supplierParts = assetAsBuiltRepository.countAssetsByOwner(Owner.SUPPLIER) + assetAsPlannedRepository.countAssetsByOwner(Owner.SUPPLIER); - long otherParts = customerParts + supplierParts; - long ownParts = assetAsBuiltRepository.countAssetsByOwner(Owner.OWN) + assetAsPlannedRepository.countAssetsByOwner(Owner.OWN); - long investigationsInReceivedState = investigationsRepository.countQualityNotificationEntitiesByStatus(QualityNotificationStatus.RECEIVED); - long alertsInReceivedState = alertRepository.countQualityNotificationEntitiesByStatus(QualityNotificationStatus.RECEIVED); - long alertsInSentState = alertRepository.countQualityNotificationEntitiesByStatus(QualityNotificationStatus.SENT); - long myPartsWithOpenAlerts = alertRepository.countPartsByStatusAndOwnership(List.of(QualityNotificationStatus.SENT), Owner.OWN); - long supplierPartsWithOpenAlerts = alertRepository.countPartsByStatusAndOwnership(List.of(QualityNotificationStatus.RECEIVED), Owner.SUPPLIER); + long asBuiltCustomerParts = assetAsBuiltRepository.countAssetsByOwner(Owner.CUSTOMER); + long asPlannedCustomerParts = assetAsPlannedRepository.countAssetsByOwner(Owner.CUSTOMER); + long asBuiltSupplierParts = assetAsBuiltRepository.countAssetsByOwner(Owner.SUPPLIER); + long asPlannedSupplierParts = assetAsPlannedRepository.countAssetsByOwner(Owner.SUPPLIER); + long asBuiltOwnParts = assetAsBuiltRepository.countAssetsByOwner(Owner.OWN); + long asPlannedOwnParts = assetAsPlannedRepository.countAssetsByOwner(Owner.OWN); + + + long myPartsWithSentAlerts = alertRepository.countOpenNotificationsByOwnership(List.of(Owner.OWN)); + long myPartsWithReceivedInvestigations = investigationsRepository.countOpenNotificationsByOwnership(List.of(Owner.OWN)); + + long otherPartsWithOpenReceivedAlerts = alertRepository.countOpenNotificationsByOwnership(List.of(Owner.CUSTOMER, Owner.SUPPLIER)); + long otherPartsWithOpenSentInvestigations = investigationsRepository.countOpenNotificationsByOwnership(List.of(Owner.CUSTOMER, Owner.SUPPLIER)); return Dashboard.builder() - .myParts(ownParts) - .otherParts(otherParts) - .investigationsReceived(investigationsInReceivedState) - .alertsReceived(alertsInReceivedState) - .alertsSent(alertsInSentState) - .myPartsWithOpenAlerts(myPartsWithOpenAlerts) - .supplierPartsWithOpenAlerts(supplierPartsWithOpenAlerts) + .asBuiltCustomerParts(asBuiltCustomerParts) + .asPlannedCustomerParts(asPlannedCustomerParts) + .asBuiltSupplierParts(asBuiltSupplierParts) + .asPlannedSupplierParts(asPlannedSupplierParts) + .asBuiltOwnParts(asBuiltOwnParts) + .asPlannedOwnParts(asPlannedOwnParts) + .myPartsWithOpenAlerts(myPartsWithSentAlerts) + .myPartsWithOpenInvestigations(myPartsWithReceivedInvestigations) + .otherPartsWithOpenAlerts(otherPartsWithOpenReceivedAlerts) + .otherPartsWithOpenInvestigations(otherPartsWithOpenSentInvestigations) .build(); + } } diff --git a/tx-backend/src/main/java/org/eclipse/tractusx/traceability/assets/infrastructure/asbuilt/repository/AssetAsBuiltRepositoryImpl.java b/tx-backend/src/main/java/org/eclipse/tractusx/traceability/assets/infrastructure/asbuilt/repository/AssetAsBuiltRepositoryImpl.java index 519cd70cfb..2fee5038b2 100644 --- a/tx-backend/src/main/java/org/eclipse/tractusx/traceability/assets/infrastructure/asbuilt/repository/AssetAsBuiltRepositoryImpl.java +++ b/tx-backend/src/main/java/org/eclipse/tractusx/traceability/assets/infrastructure/asbuilt/repository/AssetAsBuiltRepositoryImpl.java @@ -135,4 +135,9 @@ public long countAssets() { public long countAssetsByOwner(Owner owner) { return jpaAssetAsBuiltRepository.countAssetsByOwner(owner); } + + @Override + public List findByOwner(Owner owner) { + return jpaAssetAsBuiltRepository.findByOwner(owner); + } } diff --git a/tx-backend/src/main/java/org/eclipse/tractusx/traceability/assets/infrastructure/asbuilt/repository/JpaAssetAsBuiltRepository.java b/tx-backend/src/main/java/org/eclipse/tractusx/traceability/assets/infrastructure/asbuilt/repository/JpaAssetAsBuiltRepository.java index 7805ab89a2..8ef38ce04f 100644 --- a/tx-backend/src/main/java/org/eclipse/tractusx/traceability/assets/infrastructure/asbuilt/repository/JpaAssetAsBuiltRepository.java +++ b/tx-backend/src/main/java/org/eclipse/tractusx/traceability/assets/infrastructure/asbuilt/repository/JpaAssetAsBuiltRepository.java @@ -23,8 +23,6 @@ import org.eclipse.tractusx.traceability.assets.domain.base.model.Owner; import org.eclipse.tractusx.traceability.assets.infrastructure.asbuilt.model.AssetAsBuiltEntity; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; import org.springframework.data.jpa.repository.Query; @@ -37,7 +35,7 @@ public interface JpaAssetAsBuiltRepository extends JpaRepository, JpaSpecificationExecutor { @Query("SELECT asset FROM AssetAsBuiltEntity asset WHERE asset.owner = :owner") - Page findByOwner(Pageable pageable, @Param("owner") Owner owner); + List findByOwner(@Param("owner") Owner owner); List findByIdIn(List assetIds); diff --git a/tx-backend/src/main/java/org/eclipse/tractusx/traceability/assets/infrastructure/asplanned/repository/AssetAsPlannedRepositoryImpl.java b/tx-backend/src/main/java/org/eclipse/tractusx/traceability/assets/infrastructure/asplanned/repository/AssetAsPlannedRepositoryImpl.java index d5b4074ef8..ff224e8e14 100644 --- a/tx-backend/src/main/java/org/eclipse/tractusx/traceability/assets/infrastructure/asplanned/repository/AssetAsPlannedRepositoryImpl.java +++ b/tx-backend/src/main/java/org/eclipse/tractusx/traceability/assets/infrastructure/asplanned/repository/AssetAsPlannedRepositoryImpl.java @@ -122,6 +122,12 @@ public long countAssetsByOwner(Owner owner) { return jpaAssetAsPlannedRepository.countAssetsByOwner(owner); } + + @Override + public List findByOwner(Owner owner) { + return jpaAssetAsPlannedRepository.findByOwner(owner); + } + @Override public List getFieldValues(String fieldName, String startWith, Long resultLimit, String owner) { String databaseFieldName = toDatabaseName(fieldName); diff --git a/tx-backend/src/main/java/org/eclipse/tractusx/traceability/assets/infrastructure/asplanned/repository/JpaAssetAsPlannedRepository.java b/tx-backend/src/main/java/org/eclipse/tractusx/traceability/assets/infrastructure/asplanned/repository/JpaAssetAsPlannedRepository.java index 9172bc0063..6319d1ebcd 100644 --- a/tx-backend/src/main/java/org/eclipse/tractusx/traceability/assets/infrastructure/asplanned/repository/JpaAssetAsPlannedRepository.java +++ b/tx-backend/src/main/java/org/eclipse/tractusx/traceability/assets/infrastructure/asplanned/repository/JpaAssetAsPlannedRepository.java @@ -19,10 +19,7 @@ package org.eclipse.tractusx.traceability.assets.infrastructure.asplanned.repository; import org.eclipse.tractusx.traceability.assets.domain.base.model.Owner; -import org.eclipse.tractusx.traceability.assets.infrastructure.asbuilt.model.AssetAsBuiltEntity; import org.eclipse.tractusx.traceability.assets.infrastructure.asplanned.model.AssetAsPlannedEntity; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; import org.springframework.data.jpa.repository.Query; @@ -32,7 +29,7 @@ public interface JpaAssetAsPlannedRepository extends JpaRepository, JpaSpecificationExecutor { @Query("SELECT asset FROM AssetAsPlannedEntity asset WHERE asset.owner = :owner") - Page findByOwner(Pageable pageable, @Param("owner") Owner owner); + List findByOwner(@Param("owner") Owner owner); List findByIdIn(List assetIds); diff --git a/tx-backend/src/main/java/org/eclipse/tractusx/traceability/qualitynotification/domain/base/model/QualityNotificationStatus.java b/tx-backend/src/main/java/org/eclipse/tractusx/traceability/qualitynotification/domain/base/model/QualityNotificationStatus.java index c8b6f2b9ac..ee13f5c815 100644 --- a/tx-backend/src/main/java/org/eclipse/tractusx/traceability/qualitynotification/domain/base/model/QualityNotificationStatus.java +++ b/tx-backend/src/main/java/org/eclipse/tractusx/traceability/qualitynotification/domain/base/model/QualityNotificationStatus.java @@ -46,7 +46,7 @@ public enum QualityNotificationStatus { private static final Set NO_TRANSITION_ALLOWED = emptySet(); private static final Map MAPPINGS; - private static final List ACTIVE_STATES = List.of(CREATED, SENT, RECEIVED, ACKNOWLEDGED, ACCEPTED, DECLINED); + public static final List ACTIVE_STATES = List.of(CREATED, SENT, RECEIVED, ACKNOWLEDGED, ACCEPTED, DECLINED); static { STATE_MACHINE = Map.of( diff --git a/tx-backend/src/main/java/org/eclipse/tractusx/traceability/qualitynotification/domain/repository/QualityNotificationRepository.java b/tx-backend/src/main/java/org/eclipse/tractusx/traceability/qualitynotification/domain/repository/QualityNotificationRepository.java index 099a13e9dc..58615c2d0b 100644 --- a/tx-backend/src/main/java/org/eclipse/tractusx/traceability/qualitynotification/domain/repository/QualityNotificationRepository.java +++ b/tx-backend/src/main/java/org/eclipse/tractusx/traceability/qualitynotification/domain/repository/QualityNotificationRepository.java @@ -21,6 +21,7 @@ package org.eclipse.tractusx.traceability.qualitynotification.domain.repository; +import org.eclipse.tractusx.traceability.assets.domain.base.model.Owner; import org.eclipse.tractusx.traceability.common.model.PageResult; import org.eclipse.tractusx.traceability.common.model.SearchCriteria; import org.eclipse.tractusx.traceability.qualitynotification.domain.base.model.QualityNotification; @@ -30,6 +31,7 @@ import org.eclipse.tractusx.traceability.qualitynotification.domain.base.model.QualityNotificationStatus; import org.springframework.data.domain.Pageable; +import java.util.List; import java.util.Optional; public interface QualityNotificationRepository { @@ -51,4 +53,6 @@ public interface QualityNotificationRepository { void updateQualityNotificationMessageEntity(QualityNotificationMessage notification); PageResult getNotifications(Pageable pageable, SearchCriteria searchCriteria); + + long countOpenNotificationsByOwnership(List owners); } diff --git a/tx-backend/src/main/java/org/eclipse/tractusx/traceability/qualitynotification/infrastructure/alert/repository/AlertsRepositoryImpl.java b/tx-backend/src/main/java/org/eclipse/tractusx/traceability/qualitynotification/infrastructure/alert/repository/AlertsRepositoryImpl.java index 054f903b6c..6e8ba9f099 100644 --- a/tx-backend/src/main/java/org/eclipse/tractusx/traceability/qualitynotification/infrastructure/alert/repository/AlertsRepositoryImpl.java +++ b/tx-backend/src/main/java/org/eclipse/tractusx/traceability/qualitynotification/infrastructure/alert/repository/AlertsRepositoryImpl.java @@ -151,6 +151,17 @@ public long countPartsByStatusAndOwnership(List statu .toList().size(); } + @Override + public long countOpenNotificationsByOwnership(List owners) { + return jpaAlertRepository.findAllByStatusIn(NotificationStatusBaseEntity.from(QualityNotificationStatus.ACTIVE_STATES)) + .stream() + .map(AlertEntity::getAssets) + .flatMap(Collection::stream) + .filter(assetAsBuiltEntity -> owners.contains(assetAsBuiltEntity.getOwner())) + .distinct() + .toList().size(); + } + @Override public Optional findByEdcNotificationId(String edcNotificationId) { return jpaAlertRepository.findByNotificationsEdcNotificationId(edcNotificationId) @@ -228,4 +239,6 @@ private List filterNotificationAssets(QualityNoti .filter(it -> notificationAffectedAssetIds.contains(it.getId())) .toList(); } + + } diff --git a/tx-backend/src/main/java/org/eclipse/tractusx/traceability/qualitynotification/infrastructure/investigation/repository/InvestigationsRepositoryImpl.java b/tx-backend/src/main/java/org/eclipse/tractusx/traceability/qualitynotification/infrastructure/investigation/repository/InvestigationsRepositoryImpl.java index ed758e3e19..5e3bd22fbb 100644 --- a/tx-backend/src/main/java/org/eclipse/tractusx/traceability/qualitynotification/infrastructure/investigation/repository/InvestigationsRepositoryImpl.java +++ b/tx-backend/src/main/java/org/eclipse/tractusx/traceability/qualitynotification/infrastructure/investigation/repository/InvestigationsRepositoryImpl.java @@ -23,6 +23,7 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.eclipse.tractusx.traceability.assets.domain.base.model.Owner; import org.eclipse.tractusx.traceability.assets.infrastructure.asbuilt.model.AssetAsBuiltEntity; import org.eclipse.tractusx.traceability.assets.infrastructure.asbuilt.repository.JpaAssetAsBuiltRepository; import org.eclipse.tractusx.traceability.common.model.PageResult; @@ -45,6 +46,7 @@ import java.time.Clock; import java.util.ArrayList; +import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Optional; @@ -82,6 +84,17 @@ public PageResult getNotifications(Pageable pageable, Searc return new PageResult<>(jpaInvestigationRepository.findAll(specification, pageable), InvestigationEntity::toDomain); } + @Override + public long countOpenNotificationsByOwnership(List owners) { + return jpaInvestigationRepository.findAllByStatusIn(NotificationStatusBaseEntity.from(QualityNotificationStatus.ACTIVE_STATES)) + .stream() + .map(InvestigationEntity::getAssets) + .flatMap(Collection::stream) + .filter(assetAsBuiltEntity -> owners.contains(assetAsBuiltEntity.getOwner())) + .distinct() + .toList().size(); + } + @Override public QualityNotificationId updateQualityNotificationEntity(QualityNotification investigation) { InvestigationEntity investigationEntity = jpaInvestigationRepository.findById(investigation.getNotificationId().value()) diff --git a/tx-backend/src/main/java/org/eclipse/tractusx/traceability/qualitynotification/infrastructure/investigation/repository/JpaInvestigationRepository.java b/tx-backend/src/main/java/org/eclipse/tractusx/traceability/qualitynotification/infrastructure/investigation/repository/JpaInvestigationRepository.java index d4d4958c5f..6e3cc66bb8 100644 --- a/tx-backend/src/main/java/org/eclipse/tractusx/traceability/qualitynotification/infrastructure/investigation/repository/JpaInvestigationRepository.java +++ b/tx-backend/src/main/java/org/eclipse/tractusx/traceability/qualitynotification/infrastructure/investigation/repository/JpaInvestigationRepository.java @@ -32,6 +32,7 @@ import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; +import java.util.List; import java.util.Optional; @Repository @@ -45,4 +46,6 @@ public interface JpaInvestigationRepository extends JpaRepository findByNotificationsEdcNotificationId(@Param("edcNotificationId") String edcNotificationId); + + List findAllByStatusIn(List statuses); } diff --git a/tx-backend/src/test/java/org/eclipse/tractusx/traceability/assets/application/rest/DashboardControllerTest.java b/tx-backend/src/test/java/org/eclipse/tractusx/traceability/assets/application/rest/DashboardControllerTest.java index e2dc2ad065..d832d64ae3 100644 --- a/tx-backend/src/test/java/org/eclipse/tractusx/traceability/assets/application/rest/DashboardControllerTest.java +++ b/tx-backend/src/test/java/org/eclipse/tractusx/traceability/assets/application/rest/DashboardControllerTest.java @@ -40,23 +40,29 @@ class DashboardControllerTest { @Test void dashboard() { Dashboard dashboard = Dashboard.builder() - .myParts(9L) - .otherParts(99L) - .investigationsReceived(999L) - .alertsReceived(1L) - .alertsSent(11L) - .myPartsWithOpenAlerts(111L) - .supplierPartsWithOpenAlerts(1111L).build(); + .asBuiltCustomerParts(9L) + .asPlannedCustomerParts(99L) + .asBuiltSupplierParts(999L) + .asPlannedSupplierParts(1L) + .asBuiltOwnParts(11L) + .asPlannedOwnParts(111L) + .myPartsWithOpenAlerts(1111L) + .myPartsWithOpenInvestigations(1111L) + .otherPartsWithOpenAlerts(1111L) + .otherPartsWithOpenInvestigations(1111L) + .build(); Mockito.when(dashboardService.getDashboard()).thenReturn(dashboard); Dashboard testDashboard = dashboardService.getDashboard(); - assertEquals(9, testDashboard.getMyParts()); - assertEquals(99, testDashboard.getOtherParts()); - assertEquals(999, testDashboard.getInvestigationsReceived()); - assertEquals(1, testDashboard.getAlertsReceived()); - assertEquals(11, testDashboard.getAlertsSent()); - assertEquals(111, testDashboard.getMyPartsWithOpenAlerts()); - assertEquals(1111, testDashboard.getSupplierPartsWithOpenAlerts()); + assertEquals(9, testDashboard.getAsBuiltCustomerParts()); + assertEquals(99, testDashboard.getAsPlannedCustomerParts()); + assertEquals(999, testDashboard.getAsBuiltSupplierParts()); + assertEquals(11, testDashboard.getAsBuiltOwnParts()); + assertEquals(111, testDashboard.getAsPlannedOwnParts()); + assertEquals(1111, testDashboard.getMyPartsWithOpenAlerts()); + assertEquals(1111, testDashboard.getMyPartsWithOpenInvestigations()); + assertEquals(1111, testDashboard.getOtherPartsWithOpenAlerts()); + assertEquals(1111, testDashboard.getOtherPartsWithOpenInvestigations()); } } diff --git a/tx-backend/src/test/java/org/eclipse/tractusx/traceability/integration/assets/DashboardControllerIT.java b/tx-backend/src/test/java/org/eclipse/tractusx/traceability/integration/assets/DashboardControllerIT.java index 9f39dbd89d..a1ae66a6f4 100644 --- a/tx-backend/src/test/java/org/eclipse/tractusx/traceability/integration/assets/DashboardControllerIT.java +++ b/tx-backend/src/test/java/org/eclipse/tractusx/traceability/integration/assets/DashboardControllerIT.java @@ -86,13 +86,18 @@ void givenRoles_whenGetDashboard_thenReturnResponse(final List roles) t .log().all() .when().get("/api/dashboard") .then().statusCode(200) - .body("myParts", equalTo(1)) - .body("otherParts", equalTo(12)) - .body("investigationsReceived", equalTo(0)) - .body("alertsReceived", equalTo(0)) - .body("alertsSent", equalTo(0)) + .log().all() + .body("asBuiltCustomerParts", equalTo(0)) + .body("asPlannedCustomerParts", equalTo(0)) + .body("asBuiltSupplierParts", equalTo(12)) + .body("asPlannedSupplierParts", equalTo(0)) + .body("asBuiltOwnParts", equalTo(1)) + .body("asPlannedOwnParts", equalTo(0)) .body("myPartsWithOpenAlerts", equalTo(0)) - .body("supplierPartsWithOpenAlerts", equalTo(0)); + .body("myPartsWithOpenInvestigations", equalTo(0)) + .body("otherPartsWithOpenAlerts", equalTo(0)) + .body("otherPartsWithOpenInvestigations", equalTo(0)) + ; } @Test @@ -116,13 +121,17 @@ void givenAlertsWithAssets_whenGetDashboard_thenReturnResponse() throws JoseExce .log().all() .when().get("/api/dashboard") .then().statusCode(200) - .body("myParts", equalTo(1)) - .body("otherParts", equalTo(12)) - .body("investigationsReceived", equalTo(0)) - .body("alertsReceived", equalTo(1)) - .body("alertsSent", equalTo(1)) + .log().all() + .body("asBuiltCustomerParts", equalTo(0)) + .body("asPlannedCustomerParts", equalTo(0)) + .body("asBuiltSupplierParts", equalTo(12)) + .body("asPlannedSupplierParts", equalTo(0)) + .body("asBuiltOwnParts", equalTo(1)) + .body("asPlannedOwnParts", equalTo(0)) .body("myPartsWithOpenAlerts", equalTo(1)) - .body("supplierPartsWithOpenAlerts", equalTo(12)); + .body("myPartsWithOpenInvestigations", equalTo(0)) + .body("otherPartsWithOpenAlerts", equalTo(12)) + .body("otherPartsWithOpenInvestigations", equalTo(0)); } @Test @@ -168,13 +177,17 @@ void givenPendingInvestigation_whenGetDashboard_thenReturnPendingInvestigation() .log().all() .when().get("/api/dashboard") .then().statusCode(200) - .body("myParts", equalTo(1)) - .body("otherParts", equalTo(12)) - .body("investigationsReceived", equalTo(1)) - .body("alertsReceived", equalTo(0)) - .body("alertsSent", equalTo(0)) + .log().all() + .body("asBuiltCustomerParts", equalTo(0)) + .body("asPlannedCustomerParts", equalTo(0)) + .body("asBuiltSupplierParts", equalTo(12)) + .body("asPlannedSupplierParts", equalTo(0)) + .body("asBuiltOwnParts", equalTo(1)) + .body("asPlannedOwnParts", equalTo(0)) .body("myPartsWithOpenAlerts", equalTo(0)) - .body("supplierPartsWithOpenAlerts", equalTo(0)); + .body("myPartsWithOpenInvestigations", equalTo(0)) + .body("otherPartsWithOpenAlerts", equalTo(0)) + .body("otherPartsWithOpenInvestigations", equalTo(1)); } private static Stream roles() { diff --git a/tx-models/src/main/java/assets/response/DashboardResponse.java b/tx-models/src/main/java/assets/response/DashboardResponse.java index d83b23ce60..bc110a2824 100644 --- a/tx-models/src/main/java/assets/response/DashboardResponse.java +++ b/tx-models/src/main/java/assets/response/DashboardResponse.java @@ -23,18 +23,24 @@ public record DashboardResponse( @ApiModelProperty(example = "5") - Long myParts, + Long asBuiltCustomerParts, @ApiModelProperty(example = "10") - Long otherParts, + Long asPlannedCustomerParts, @ApiModelProperty(example = "2") - Long investigationsReceived, + Long asBuiltSupplierParts, @ApiModelProperty(example = "3") - Long alertsReceived, + Long asPlannedSupplierParts, @ApiModelProperty(example = "1") - Long alertsSent, + Long asBuiltOwnParts, + @ApiModelProperty(example = "1") + Long asPlannedOwnParts, @ApiModelProperty(example = "1") Long myPartsWithOpenAlerts, + @ApiModelProperty(example = "1") + Long myPartsWithOpenInvestigations, + @ApiModelProperty(example = "1") + Long otherPartsWithOpenAlerts, @ApiModelProperty(example = "2") - Long supplierPartsWithOpenAlerts) { + Long otherPartsWithOpenInvestigations) { } diff --git a/tx-models/src/main/java/assets/response/asbuilt/AssetAsBuiltResponse.java b/tx-models/src/main/java/assets/response/asbuilt/AssetAsBuiltResponse.java index 68072144e0..c3a67a7735 100644 --- a/tx-models/src/main/java/assets/response/asbuilt/AssetAsBuiltResponse.java +++ b/tx-models/src/main/java/assets/response/asbuilt/AssetAsBuiltResponse.java @@ -20,13 +20,24 @@ package assets.response.asbuilt; import assets.response.base.AssetBaseResponse; +import io.swagger.annotations.ApiModelProperty; import io.swagger.v3.oas.annotations.media.ArraySchema; import io.swagger.v3.oas.annotations.media.Schema; import lombok.experimental.SuperBuilder; +import java.util.List; + @SuperBuilder @ArraySchema(arraySchema = @Schema(description = "Assets", additionalProperties = Schema.AdditionalPropertiesValue.FALSE), maxItems = Integer.MAX_VALUE) public class AssetAsBuiltResponse extends AssetBaseResponse { + @ApiModelProperty(dataType = "List", example = "1") + private List sentQualityAlertIdsInStatusActive; + @ApiModelProperty(dataType = "List", example = "1") + private List receivedQualityAlertIdsInStatusActive; + @ApiModelProperty(dataType = "List", example = "2") + private List sentQualityInvestigationIdsInStatusActive; + @ApiModelProperty(dataType = "List", example = "2") + private List receivedQualityInvestigationIdsInStatusActive; } From 8343b2e890985d8e3a79280ad3ddb226310ece97 Mon Sep 17 00:00:00 2001 From: Martin Maul Date: Tue, 21 Nov 2023 09:48:51 +0100 Subject: [PATCH 03/17] feature(UI):[TRACEFOSS-604] added alerts widget --- frontend/src/app/modules/core/core.module.ts | 57 ++++++++++--------- .../dashboard/abstraction/dashboard.facade.ts | 31 +++++----- .../page/dashboard/core/dashboard.service.ts | 16 +++--- .../page/dashboard/core/dashboard.state.ts | 18 ++++-- .../presentation/dashboard.component.html | 28 ++++----- .../presentation/dashboard.component.ts | 25 ++++---- 6 files changed, 98 insertions(+), 77 deletions(-) diff --git a/frontend/src/app/modules/core/core.module.ts b/frontend/src/app/modules/core/core.module.ts index 4739f646db..681aeaca8d 100644 --- a/frontend/src/app/modules/core/core.module.ts +++ b/frontend/src/app/modules/core/core.module.ts @@ -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], diff --git a/frontend/src/app/modules/page/dashboard/abstraction/dashboard.facade.ts b/frontend/src/app/modules/page/dashboard/abstraction/dashboard.facade.ts index 7d48fb69b3..0cb447c54f 100644 --- a/frontend/src/app/modules/page/dashboard/abstraction/dashboard.facade.ts +++ b/frontend/src/app/modules/page/dashboard/abstraction/dashboard.facade.ts @@ -19,15 +19,15 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ -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 { Observable, Subscription } from 'rxjs'; -import { DashboardService } from '../core/dashboard.service'; -import { DashboardState } from '../core/dashboard.state'; -import { DashboardStats } from '../model/dashboard.model'; +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 {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 { @@ -43,15 +43,15 @@ export class DashboardFacade { private readonly alertsService: AlertsService ) {} - public get numberOfMyParts$(): Observable> { + public get numberOfTotalMyParts$(): Observable> { return this.dashboardState.numberOfTotalMyParts$; } -/* - public get numberOfOtherParts$(): Observable> { - return this.dashboardState.numberOfAsBuiltSupplierParts$.subscribe(next => next.data as number) + + public get numberOfTotalOtherParts$(): Observable> { + return this.dashboardState.numberOfTotalOtherParts$; } - */ + public get numberOfMyPartsWithOpenInvestigations$(): Observable> { return this.dashboardState.numberOfMyPartsWithOpenInvestigations$; } @@ -72,6 +72,7 @@ export class DashboardFacade { private setAssetNumbers(): void { this.dashboardState.setNumberOfTotalMyParts({ loader: true }); + this.dashboardState.setNumberOfTotalOtherParts({loader: true}); this.dashboardState.setNumberOfAsBuiltOwnParts({loader: true}); this.dashboardState.setNumberOfAsPlannedOwnParts({loader: true}); @@ -89,6 +90,7 @@ export class DashboardFacade { this.assetNumbersSubscription = this.dashboardService.getStats().subscribe({ next: (dashboardStats: DashboardStats) => { this.dashboardState.setNumberOfTotalMyParts({ data: dashboardStats.totalOwnParts }); + this.dashboardState.setNumberOfTotalOtherParts({data: dashboardStats.totalOtherParts}); this.dashboardState.setNumberOfAsBuiltOwnParts({data: dashboardStats.asBuiltOwnParts}); this.dashboardState.setNumberOfAsPlannedOwnParts({data: dashboardStats.asPlannedOwnParts}); @@ -104,6 +106,7 @@ export class DashboardFacade { }, error: error => { this.dashboardState.setNumberOfTotalMyParts({ error }); + this.dashboardState.setNumberOfTotalOtherParts({error}); this.dashboardState.setNumberOfAsBuiltOwnParts({error}); this.dashboardState.setNumberOfAsPlannedOwnParts({error}); diff --git a/frontend/src/app/modules/page/dashboard/core/dashboard.service.ts b/frontend/src/app/modules/page/dashboard/core/dashboard.service.ts index 647da41772..1a510c8da4 100644 --- a/frontend/src/app/modules/page/dashboard/core/dashboard.service.ts +++ b/frontend/src/app/modules/page/dashboard/core/dashboard.service.ts @@ -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 { @@ -36,6 +36,6 @@ export class DashboardService { public getStats(): Observable { return this.apiService .get(`${this.url}/dashboard`) - .pipe(map((payload: DashboardStatsResponse) => DashboardAssembler.assembleDashboard(payload))) + .pipe(map((payload: DashboardStatsResponse) => DashboardAssembler.assembleDashboard(payload))); } } diff --git a/frontend/src/app/modules/page/dashboard/core/dashboard.state.ts b/frontend/src/app/modules/page/dashboard/core/dashboard.state.ts index 676dec0577..9bca023f2f 100644 --- a/frontend/src/app/modules/page/dashboard/core/dashboard.state.ts +++ b/frontend/src/app/modules/page/dashboard/core/dashboard.state.ts @@ -19,12 +19,12 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ -import { Injectable } from '@angular/core'; +import {Injectable} from '@angular/core'; -import { Notifications } from '@shared/model/notification.model'; -import { State } from '@shared/model/state'; -import { Observable } from 'rxjs'; -import { View } from 'src/app/modules/shared/model/view.model'; +import {Notifications} from '@shared/model/notification.model'; +import {State} from '@shared/model/state'; +import {Observable} from 'rxjs'; +import {View} from 'src/app/modules/shared/model/view.model'; @Injectable() export class DashboardState { @@ -112,6 +112,14 @@ export class DashboardState { this._numberOfTotalMyParts$.update(count); } + public get numberOfTotalOtherParts$(): Observable> { + return this._numberOfTotalOtherParts$.observable; + } + + public setNumberOfTotalOtherParts(count: View): void { + this._numberOfTotalOtherParts$.update(count); + } + /** * part notifications getter/setter diff --git a/frontend/src/app/modules/page/dashboard/presentation/dashboard.component.html b/frontend/src/app/modules/page/dashboard/presentation/dashboard.component.html index 16583c1d9a..e182d8e7c7 100644 --- a/frontend/src/app/modules/page/dashboard/presentation/dashboard.component.html +++ b/frontend/src/app/modules/page/dashboard/presentation/dashboard.component.html @@ -23,7 +23,7 @@
+

+ find_in_page + {{ 'pageTitle.investigations' | i18n }} +

-
-

- find_in_page - {{ 'pageTitle.investigations' | i18n }} -

+
+
+

+ find_in_page + {{ 'pageTitle.alerts' | i18n }} +

-
-

- find_in_page - {{ 'pageTitle.alerts' | i18n }} -

+
+
- + diff --git a/frontend/src/app/modules/page/dashboard/presentation/dashboard.component.ts b/frontend/src/app/modules/page/dashboard/presentation/dashboard.component.ts index 317183634f..01c62df8e1 100644 --- a/frontend/src/app/modules/page/dashboard/presentation/dashboard.component.ts +++ b/frontend/src/app/modules/page/dashboard/presentation/dashboard.component.ts @@ -19,19 +19,21 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ -import { Component, OnDestroy, OnInit, ViewChild } from '@angular/core'; -import { Router } from '@angular/router'; -import { ALERT_BASE_ROUTE, getRoute, INVESTIGATION_BASE_ROUTE } from '@core/known-route'; +import {Component, OnDestroy, OnInit, ViewChild} from '@angular/core'; +import {Router} from '@angular/router'; +import {ALERT_BASE_ROUTE, getRoute, INVESTIGATION_BASE_ROUTE} from '@core/known-route'; import { Notification, Notifications, NotificationStatusGroup, NotificationType, } from '@shared/model/notification.model'; -import { View } from '@shared/model/view.model'; -import { CloseNotificationModalComponent } from '@shared/modules/notification/modal/close/close-notification-modal.component'; -import { Observable } from 'rxjs'; -import { DashboardFacade } from '../abstraction/dashboard.facade'; +import {View} from '@shared/model/view.model'; +import { + CloseNotificationModalComponent +} from '@shared/modules/notification/modal/close/close-notification-modal.component'; +import {Observable} from 'rxjs'; +import {DashboardFacade} from '../abstraction/dashboard.facade'; @Component({ selector: 'app-dashboard', @@ -41,8 +43,8 @@ import { DashboardFacade } from '../abstraction/dashboard.facade'; export class DashboardComponent implements OnInit, OnDestroy { @ViewChild(CloseNotificationModalComponent) private closeModal: CloseNotificationModalComponent; - public readonly numberOfMyParts$: Observable>; - public readonly numberOfOtherParts$: Observable>; + public readonly numberOfTotalMyParts$: Observable>; + public readonly numberOfTotalOtherParts$: Observable>; public readonly numberOfInvestigations$: Observable>; public readonly investigations$: Observable>; @@ -55,8 +57,9 @@ export class DashboardComponent implements OnInit, OnDestroy { public readonly alertParams: Record; constructor(private readonly dashboardFacade: DashboardFacade, private readonly router: Router) { - this.numberOfMyParts$ = this.dashboardFacade.numberOfMyParts$; - //this.numberOfOtherParts$ = this.dashboardFacade.numberOfOtherParts$; + this.numberOfTotalMyParts$ = this.dashboardFacade.numberOfTotalMyParts$; + this.numberOfTotalOtherParts$ = this.dashboardFacade.numberOfTotalOtherParts$; + this.numberOfInvestigations$ = this.dashboardFacade.numberOfMyPartsWithOpenInvestigations$; this.investigations$ = this.dashboardFacade.recentInvestigations$; From 01b39334a7a9354059ea571ec1437d47342ed584 Mon Sep 17 00:00:00 2001 From: ds-lcapellino Date: Tue, 21 Nov 2023 09:51:10 +0100 Subject: [PATCH 04/17] feature: TRACEFOSS-604 update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 10ee044baf..91e3ac96dc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ### Changed - Fixed helm repository path for backend & frontend (wrong prefix) +- Refactored dashboard response ### Removed - apk upgrade in docker image built as requested by TRG 4.02 From f3762c7e4ceac91257dd1a3405f337bc6cb6b16b Mon Sep 17 00:00:00 2001 From: ds-lcapellino Date: Tue, 21 Nov 2023 13:17:23 +0100 Subject: [PATCH 05/17] feature: TRACEFOSS-604 update CHANGELOG.md --- .../mapper/DashboardResponseMapper.java | 6 ++++-- .../domain/dashboard/model/Dashboard.java | 6 ++++-- .../service/DashboardServiceImpl.java | 13 +++++++++---- .../rest/DashboardControllerTest.java | 12 ++++++++---- .../assets/DashboardControllerIT.java | 19 ++++++++++++------- .../assets/response/DashboardResponse.java | 8 ++++++-- 6 files changed, 43 insertions(+), 21 deletions(-) diff --git a/tx-backend/src/main/java/org/eclipse/tractusx/traceability/assets/application/dashboard/mapper/DashboardResponseMapper.java b/tx-backend/src/main/java/org/eclipse/tractusx/traceability/assets/application/dashboard/mapper/DashboardResponseMapper.java index 56f9a65c9a..3846a69ee5 100644 --- a/tx-backend/src/main/java/org/eclipse/tractusx/traceability/assets/application/dashboard/mapper/DashboardResponseMapper.java +++ b/tx-backend/src/main/java/org/eclipse/tractusx/traceability/assets/application/dashboard/mapper/DashboardResponseMapper.java @@ -33,8 +33,10 @@ public static DashboardResponse from(final Dashboard dashboard) { dashboard.getAsPlannedOwnParts(), dashboard.getMyPartsWithOpenAlerts(), dashboard.getMyPartsWithOpenInvestigations(), - dashboard.getOtherPartsWithOpenAlerts(), - dashboard.getOtherPartsWithOpenInvestigations() + dashboard.getSupplierPartsWithOpenAlerts(), + dashboard.getCustomerPartsWithOpenAlerts(), + dashboard.getSupplierPartsWithOpenInvestigations(), + dashboard.getCustomerPartsWithOpenInvestigations() ); } } diff --git a/tx-backend/src/main/java/org/eclipse/tractusx/traceability/assets/domain/dashboard/model/Dashboard.java b/tx-backend/src/main/java/org/eclipse/tractusx/traceability/assets/domain/dashboard/model/Dashboard.java index 479a6c7cfe..f8e22a4157 100644 --- a/tx-backend/src/main/java/org/eclipse/tractusx/traceability/assets/domain/dashboard/model/Dashboard.java +++ b/tx-backend/src/main/java/org/eclipse/tractusx/traceability/assets/domain/dashboard/model/Dashboard.java @@ -35,6 +35,8 @@ public class Dashboard { long asPlannedOwnParts; long myPartsWithOpenAlerts; long myPartsWithOpenInvestigations; - long otherPartsWithOpenAlerts; - long otherPartsWithOpenInvestigations; + long supplierPartsWithOpenAlerts; + long customerPartsWithOpenAlerts; + long supplierPartsWithOpenInvestigations; + long customerPartsWithOpenInvestigations; } diff --git a/tx-backend/src/main/java/org/eclipse/tractusx/traceability/assets/domain/dashboard/service/DashboardServiceImpl.java b/tx-backend/src/main/java/org/eclipse/tractusx/traceability/assets/domain/dashboard/service/DashboardServiceImpl.java index ebc33946ed..d4dcc99be1 100644 --- a/tx-backend/src/main/java/org/eclipse/tractusx/traceability/assets/domain/dashboard/service/DashboardServiceImpl.java +++ b/tx-backend/src/main/java/org/eclipse/tractusx/traceability/assets/domain/dashboard/service/DashboardServiceImpl.java @@ -55,8 +55,11 @@ public Dashboard getDashboard() { long myPartsWithSentAlerts = alertRepository.countOpenNotificationsByOwnership(List.of(Owner.OWN)); long myPartsWithReceivedInvestigations = investigationsRepository.countOpenNotificationsByOwnership(List.of(Owner.OWN)); - long otherPartsWithOpenReceivedAlerts = alertRepository.countOpenNotificationsByOwnership(List.of(Owner.CUSTOMER, Owner.SUPPLIER)); - long otherPartsWithOpenSentInvestigations = investigationsRepository.countOpenNotificationsByOwnership(List.of(Owner.CUSTOMER, Owner.SUPPLIER)); + long supplierPartsWithOpenReceivedAlerts = alertRepository.countOpenNotificationsByOwnership(List.of(Owner.SUPPLIER)); + long supplierPartsWithOpenSentInvestigations = investigationsRepository.countOpenNotificationsByOwnership(List.of(Owner.SUPPLIER)); + + long customerPartsWithOpenReceivedAlerts = alertRepository.countOpenNotificationsByOwnership(List.of(Owner.CUSTOMER)); + long customerPartsWithOpenSentInvestigations = investigationsRepository.countOpenNotificationsByOwnership(List.of(Owner.CUSTOMER)); return Dashboard.builder() .asBuiltCustomerParts(asBuiltCustomerParts) @@ -67,8 +70,10 @@ public Dashboard getDashboard() { .asPlannedOwnParts(asPlannedOwnParts) .myPartsWithOpenAlerts(myPartsWithSentAlerts) .myPartsWithOpenInvestigations(myPartsWithReceivedInvestigations) - .otherPartsWithOpenAlerts(otherPartsWithOpenReceivedAlerts) - .otherPartsWithOpenInvestigations(otherPartsWithOpenSentInvestigations) + .supplierPartsWithOpenAlerts(supplierPartsWithOpenReceivedAlerts) + .customerPartsWithOpenAlerts(customerPartsWithOpenReceivedAlerts) + .supplierPartsWithOpenInvestigations(supplierPartsWithOpenSentInvestigations) + .customerPartsWithOpenInvestigations(customerPartsWithOpenSentInvestigations) .build(); } diff --git a/tx-backend/src/test/java/org/eclipse/tractusx/traceability/assets/application/rest/DashboardControllerTest.java b/tx-backend/src/test/java/org/eclipse/tractusx/traceability/assets/application/rest/DashboardControllerTest.java index d832d64ae3..5a15ab01a8 100644 --- a/tx-backend/src/test/java/org/eclipse/tractusx/traceability/assets/application/rest/DashboardControllerTest.java +++ b/tx-backend/src/test/java/org/eclipse/tractusx/traceability/assets/application/rest/DashboardControllerTest.java @@ -48,8 +48,10 @@ void dashboard() { .asPlannedOwnParts(111L) .myPartsWithOpenAlerts(1111L) .myPartsWithOpenInvestigations(1111L) - .otherPartsWithOpenAlerts(1111L) - .otherPartsWithOpenInvestigations(1111L) + .supplierPartsWithOpenAlerts(1111L) + .customerPartsWithOpenAlerts(1111L) + .supplierPartsWithOpenInvestigations(1111L) + .customerPartsWithOpenInvestigations(1111L) .build(); Mockito.when(dashboardService.getDashboard()).thenReturn(dashboard); Dashboard testDashboard = dashboardService.getDashboard(); @@ -61,8 +63,10 @@ void dashboard() { assertEquals(111, testDashboard.getAsPlannedOwnParts()); assertEquals(1111, testDashboard.getMyPartsWithOpenAlerts()); assertEquals(1111, testDashboard.getMyPartsWithOpenInvestigations()); - assertEquals(1111, testDashboard.getOtherPartsWithOpenAlerts()); - assertEquals(1111, testDashboard.getOtherPartsWithOpenInvestigations()); + assertEquals(1111, testDashboard.getSupplierPartsWithOpenAlerts()); + assertEquals(1111, testDashboard.getCustomerPartsWithOpenAlerts()); + assertEquals(1111, testDashboard.getSupplierPartsWithOpenInvestigations()); + assertEquals(1111, testDashboard.getCustomerPartsWithOpenInvestigations()); } } diff --git a/tx-backend/src/test/java/org/eclipse/tractusx/traceability/integration/assets/DashboardControllerIT.java b/tx-backend/src/test/java/org/eclipse/tractusx/traceability/integration/assets/DashboardControllerIT.java index a1ae66a6f4..7a0edf977a 100644 --- a/tx-backend/src/test/java/org/eclipse/tractusx/traceability/integration/assets/DashboardControllerIT.java +++ b/tx-backend/src/test/java/org/eclipse/tractusx/traceability/integration/assets/DashboardControllerIT.java @@ -95,9 +95,10 @@ void givenRoles_whenGetDashboard_thenReturnResponse(final List roles) t .body("asPlannedOwnParts", equalTo(0)) .body("myPartsWithOpenAlerts", equalTo(0)) .body("myPartsWithOpenInvestigations", equalTo(0)) - .body("otherPartsWithOpenAlerts", equalTo(0)) - .body("otherPartsWithOpenInvestigations", equalTo(0)) - ; + .body("supplierPartsWithOpenAlerts", equalTo(0)) + .body("customerPartsWithOpenAlerts", equalTo(0)) + .body("supplierPartsWithOpenInvestigations", equalTo(0)) + .body("customerPartsWithOpenInvestigations", equalTo(0)); } @Test @@ -130,8 +131,10 @@ void givenAlertsWithAssets_whenGetDashboard_thenReturnResponse() throws JoseExce .body("asPlannedOwnParts", equalTo(0)) .body("myPartsWithOpenAlerts", equalTo(1)) .body("myPartsWithOpenInvestigations", equalTo(0)) - .body("otherPartsWithOpenAlerts", equalTo(12)) - .body("otherPartsWithOpenInvestigations", equalTo(0)); + .body("supplierPartsWithOpenAlerts", equalTo(12)) + .body("customerPartsWithOpenAlerts", equalTo(0)) + .body("supplierPartsWithOpenInvestigations", equalTo(0)) + .body("customerPartsWithOpenInvestigations", equalTo(0)); } @Test @@ -186,8 +189,10 @@ void givenPendingInvestigation_whenGetDashboard_thenReturnPendingInvestigation() .body("asPlannedOwnParts", equalTo(0)) .body("myPartsWithOpenAlerts", equalTo(0)) .body("myPartsWithOpenInvestigations", equalTo(0)) - .body("otherPartsWithOpenAlerts", equalTo(0)) - .body("otherPartsWithOpenInvestigations", equalTo(1)); + .body("supplierPartsWithOpenAlerts", equalTo(0)) + .body("customerPartsWithOpenAlerts", equalTo(0)) + .body("supplierPartsWithOpenInvestigations", equalTo(1)) + .body("customerPartsWithOpenInvestigations", equalTo(0)); } private static Stream roles() { diff --git a/tx-models/src/main/java/assets/response/DashboardResponse.java b/tx-models/src/main/java/assets/response/DashboardResponse.java index bc110a2824..0992419891 100644 --- a/tx-models/src/main/java/assets/response/DashboardResponse.java +++ b/tx-models/src/main/java/assets/response/DashboardResponse.java @@ -39,8 +39,12 @@ public record DashboardResponse( @ApiModelProperty(example = "1") Long myPartsWithOpenInvestigations, @ApiModelProperty(example = "1") - Long otherPartsWithOpenAlerts, + Long supplierPartsWithOpenAlerts, + @ApiModelProperty(example = "1") + Long customerPartsWithOpenAlerts, + @ApiModelProperty(example = "2") + Long supplierPartsWithOpenInvestigations, @ApiModelProperty(example = "2") - Long otherPartsWithOpenInvestigations) { + Long customerPartsWithOpenInvestigations) { } From ed416d4d8ffad216a1156424127f5b72ab5f2958 Mon Sep 17 00:00:00 2001 From: Martin Maul Date: Tue, 21 Nov 2023 13:40:36 +0100 Subject: [PATCH 06/17] feature(UI):[TRACEFOSS-604] added split of notifications into received and sent --- .../dashboard-mock/dashboard.model.ts | 6 +- .../dashboard/abstraction/dashboard.facade.ts | 71 ++++-- .../page/dashboard/core/dashboard.state.ts | 40 +++- .../presentation/dashboard.component.html | 226 +++++++++++++----- .../presentation/dashboard.component.ts | 14 +- .../shared/pipes/abbreviate-number.pipe.ts | 25 ++ .../src/app/modules/shared/shared.module.ts | 109 +++++---- 7 files changed, 345 insertions(+), 146 deletions(-) create mode 100644 frontend/src/app/modules/shared/pipes/abbreviate-number.pipe.ts diff --git a/frontend/src/app/mocks/services/dashboard-mock/dashboard.model.ts b/frontend/src/app/mocks/services/dashboard-mock/dashboard.model.ts index 82f60d1723..b27e30b33e 100644 --- a/frontend/src/app/mocks/services/dashboard-mock/dashboard.model.ts +++ b/frontend/src/app/mocks/services/dashboard-mock/dashboard.model.ts @@ -19,7 +19,7 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ -import { DashboardStatsResponse } from '@page/dashboard/model/dashboard.model'; +import {DashboardStatsResponse} from '@page/dashboard/model/dashboard.model'; export const mockDashboardStats: DashboardStatsResponse = { // notification counts (where open means notficaiton status not closed) @@ -32,6 +32,6 @@ export const mockDashboardStats: DashboardStatsResponse = { asPlannedCustomerParts: 5, asBuiltSupplierParts: 500, asPlannedSupplierParts: 5000, - asBuiltOwnParts: 10000, - asPlannedOwnParts: 555555 + asBuiltOwnParts: 120543, + asPlannedOwnParts: 11203 }; diff --git a/frontend/src/app/modules/page/dashboard/abstraction/dashboard.facade.ts b/frontend/src/app/modules/page/dashboard/abstraction/dashboard.facade.ts index 0cb447c54f..ef049bcbaf 100644 --- a/frontend/src/app/modules/page/dashboard/abstraction/dashboard.facade.ts +++ b/frontend/src/app/modules/page/dashboard/abstraction/dashboard.facade.ts @@ -32,8 +32,11 @@ import {DashboardStats} from '../model/dashboard.model'; @Injectable() export class DashboardFacade { private assetNumbersSubscription: Subscription; - private investigationSubscription: Subscription; - private alertsSubscription: Subscription; + private investigationsReceivedSubscription: Subscription; + private investigationsCreatedSubscription: Subscription; + private alertsReceivedSubscription: Subscription; + private alertsCreatedSubscription: Subscription; + constructor( private readonly dashboardService: DashboardService, @@ -56,18 +59,28 @@ export class DashboardFacade { return this.dashboardState.numberOfMyPartsWithOpenInvestigations$; } - public get recentInvestigations$(): Observable> { - return this.dashboardState.recentInvestigations$; + public get recentReceivedInvestigations$(): Observable> { + return this.dashboardState.recentReceivedInvestigations$; + } + + public get recentCreatedInvestigations$(): Observable> { + return this.dashboardState.recentCreatedInvestigations$; } - public get recentAlerts$(): Observable> { - return this.dashboardState.recentAlerts$; + public get recentReceivedAlerts$(): Observable> { + return this.dashboardState.recentReceivedAlerts$; + } + + public get recentCreatedAlerts$(): Observable> { + return this.dashboardState.recentCreatedAlerts$; } public setDashboardData(): void { this.setAssetNumbers(); - this.setInvestigations(); - this.setAlerts(); + this.setReceivedInvestigations(); + this.setCreatedInvestigations(); + this.setReceivedAlerts(); + this.setCreatedAlerts(); } private setAssetNumbers(): void { @@ -125,22 +138,42 @@ export class DashboardFacade { public stopDataLoading(): void { this.assetNumbersSubscription?.unsubscribe(); - this.investigationSubscription?.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 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 setInvestigations(): void { - this.investigationSubscription?.unsubscribe(); - this.investigationSubscription = this.investigationsService.getReceivedInvestigations(0, 5, [['createdDate','desc']]).subscribe({ - next: data => this.dashboardState.setInvestigationsReceived({ data }), - error: (error: Error) => this.dashboardState.setInvestigationsReceived({ 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 setAlerts(): void { - this.alertsSubscription?.unsubscribe(); - this.alertsSubscription = this.alertsService.getReceivedAlerts(0, 5, [['createdDate','desc']]).subscribe({ - next: data => this.dashboardState.setRecentAlerts({data}), - error: (error: Error) => this.dashboardState.setRecentAlerts({ 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 }), }); } } diff --git a/frontend/src/app/modules/page/dashboard/core/dashboard.state.ts b/frontend/src/app/modules/page/dashboard/core/dashboard.state.ts index 9bca023f2f..01f1136ecc 100644 --- a/frontend/src/app/modules/page/dashboard/core/dashboard.state.ts +++ b/frontend/src/app/modules/page/dashboard/core/dashboard.state.ts @@ -49,8 +49,11 @@ export class DashboardState { // notifications received - private readonly _recentInvestigations$: State> = new State>({ loader: true }); - private readonly _recentAlerts$: State> = new State>({ loader: true }); + private readonly _recentReceivedInvestigations$: State> = new State>({ loader: true }); + private readonly _recentCreatedInvestigations$: State> = new State>({ loader: true }); + + private readonly _recentReceivedAlerts$: State> = new State>({ loader: true }); + private readonly _recentCreatedAlerts$: State> = new State>({ loader: true }); /** * part counts getter/setter @@ -163,19 +166,36 @@ export class DashboardState { */ - public get recentInvestigations$(): Observable> { - return this._recentInvestigations$.observable; + public get recentReceivedInvestigations$(): Observable> { + return this._recentReceivedInvestigations$.observable; + } + + public setRecentReceivedInvestigations(investigations: View): void { + this._recentReceivedInvestigations$.update(investigations); + } + + public get recentCreatedInvestigations$(): Observable> { + return this._recentCreatedInvestigations$.observable; + } + + public setRecentCreatedInvestigations(investigations: View): void { + this._recentCreatedInvestigations$.update(investigations); } - public setInvestigationsReceived(investigations: View): void { - this._recentInvestigations$.update(investigations); + public get recentReceivedAlerts$(): Observable> { + return this._recentReceivedAlerts$.observable; } - public get recentAlerts$(): Observable> { - return this._recentAlerts$.observable; + public setRecentReceivedAlerts(alerts: View): void { + this._recentReceivedAlerts$.update(alerts); } - public setRecentAlerts(alerts: View): void { - this._recentAlerts$.update(alerts); + public get recentCreatedAlerts$(): Observable> { + return this._recentCreatedAlerts$.observable; } + + public setRecentCreatedAlerts(alerts: View): void { + this._recentCreatedAlerts$.update(alerts); + } + } diff --git a/frontend/src/app/modules/page/dashboard/presentation/dashboard.component.html b/frontend/src/app/modules/page/dashboard/presentation/dashboard.component.html index e182d8e7c7..69211a4249 100644 --- a/frontend/src/app/modules/page/dashboard/presentation/dashboard.component.html +++ b/frontend/src/app/modules/page/dashboard/presentation/dashboard.component.html @@ -58,6 +58,7 @@ >
+
@@ -66,38 +67,87 @@

{{ 'pageTitle.investigations' | i18n }}

- - -
-
- - -
- remove_red_eye - {{ 'commonInvestigation.viewAll' | i18n }} + + + + drafts{{ 'commonInvestigation.tabs.received' | i18n }} + + + +
+
+ + +
+ remove_red_eye + {{ 'commonInvestigation.viewAll' | i18n }} +
+
+
+
+
+ + + + +
+
+ + + drafts{{ 'commonInvestigation.tabs.queuedAndRequested' | i18n }} + + + +
+
+ + +
+ remove_red_eye + {{ 'commonInvestigation.viewAll' | i18n }} +
+
- -
-
- - - - - - +
+ + + + + + + +
@@ -110,38 +160,88 @@

{{ 'pageTitle.alerts' | i18n }}

- - -
-
- - -
- remove_red_eye - {{ 'commonInvestigation.viewAll' | i18n }} + + + + drafts{{ 'commonAlert.tabs.received' | i18n }} + + + +
+
+ + +
+ remove_red_eye + {{ 'commonAlert.viewAll' | i18n }} +
+
+
+
+
+ + + + +
+
+ + + drafts{{ 'commonAlert.tabs.queuedAndRequested' | i18n }} + + + +
+
+ + +
+ remove_red_eye + {{ 'commonAlert.viewAll' | i18n }} +
+
- -
-
- - - - - - +
+ + + + + + + + +
@@ -149,7 +249,7 @@

- + diff --git a/frontend/src/app/modules/page/dashboard/presentation/dashboard.component.ts b/frontend/src/app/modules/page/dashboard/presentation/dashboard.component.ts index 01c62df8e1..a0075acce7 100644 --- a/frontend/src/app/modules/page/dashboard/presentation/dashboard.component.ts +++ b/frontend/src/app/modules/page/dashboard/presentation/dashboard.component.ts @@ -47,8 +47,10 @@ export class DashboardComponent implements OnInit, OnDestroy { public readonly numberOfTotalOtherParts$: Observable>; public readonly numberOfInvestigations$: Observable>; - public readonly investigations$: Observable>; - public readonly alerts$: Observable>; + public readonly investigationsReceived$: Observable>; + public readonly investigationsCreated$: Observable>; + public readonly alertsReceived$: Observable>; + public readonly alertsCreated$: Observable>; public readonly investigationLink: string; public readonly investigationParams: Record; @@ -62,8 +64,10 @@ export class DashboardComponent implements OnInit, OnDestroy { this.numberOfInvestigations$ = this.dashboardFacade.numberOfMyPartsWithOpenInvestigations$; - this.investigations$ = this.dashboardFacade.recentInvestigations$; - this.alerts$ = this.dashboardFacade.recentAlerts$ + this.investigationsReceived$ = this.dashboardFacade.recentReceivedInvestigations$; + this.investigationsCreated$ = this.dashboardFacade.recentCreatedInvestigations$; + this.alertsReceived$ = this.dashboardFacade.recentReceivedAlerts$ + this.alertsCreated$ = this.dashboardFacade.recentCreatedAlerts$ const { link: investigationLink, queryParams: investigationQueryParams } = getRoute(INVESTIGATION_BASE_ROUTE, NotificationStatusGroup.RECEIVED); const { link: alertLink, queryParams: alertQueryParams } = getRoute(ALERT_BASE_ROUTE, NotificationStatusGroup.RECEIVED); @@ -71,6 +75,8 @@ export class DashboardComponent implements OnInit, OnDestroy { this.investigationParams = investigationQueryParams; this.alertLink = alertLink; this.alertParams = alertQueryParams; + + console.log(this.investigationsReceived$.subscribe((next) => console.log(next))); } public ngOnInit(): void { diff --git a/frontend/src/app/modules/shared/pipes/abbreviate-number.pipe.ts b/frontend/src/app/modules/shared/pipes/abbreviate-number.pipe.ts new file mode 100644 index 0000000000..4afae9a572 --- /dev/null +++ b/frontend/src/app/modules/shared/pipes/abbreviate-number.pipe.ts @@ -0,0 +1,25 @@ +import {Inject, LOCALE_ID, Pipe, PipeTransform} from "@angular/core"; + +@Pipe({ name: 'abbreviateNumber', pure: false }) +export class AbbreviateNumberPipe implements PipeTransform { + constructor(@Inject(LOCALE_ID) private locale: string) {} + + transform(value: number | string): string { + // Convert value to a number if it's a string + const numericValue = typeof value === 'string' ? parseFloat(value.replace(/,/g, '')) : value; + + if (numericValue >= 1000000) { + if (this.locale === 'de') { + return (numericValue / 1000000).toFixed(1) + ' Mio.'; + } else if (this.locale === 'en') { + return (numericValue / 1000000).toFixed(1) + ' M.'; + } else { + return String(numericValue); + } + } else if (numericValue > 9999999) { + return 'Out of Range'; + } else { + return String(numericValue); + } + } +} diff --git a/frontend/src/app/modules/shared/shared.module.ts b/frontend/src/app/modules/shared/shared.module.ts index 830b77d37c..2541004d20 100644 --- a/frontend/src/app/modules/shared/shared.module.ts +++ b/frontend/src/app/modules/shared/shared.module.ts @@ -23,11 +23,15 @@ import {DatePipe, TitleCasePipe} from '@angular/common'; import {NgModule} from '@angular/core'; import {MatPaginatorIntl} from '@angular/material/paginator'; import {RouterModule} from '@angular/router'; -import {BomLifecycleActivatorComponent } from '@shared/components/bom-lifecycle-activator/bom-lifecycle-activator.component'; -import { CountryFlagGeneratorComponent } from '@shared/components/country-flag-generator/country-flag-generator.component'; -import { DateTimeComponent } from '@shared/components/dateTime/dateTime.component'; -import { FormErrorMessageComponent } from '@shared/components/formErrorMessage/formErrorMessage.component'; -import { InputComponent } from '@shared/components/input/input.component'; +import { + BomLifecycleActivatorComponent +} from '@shared/components/bom-lifecycle-activator/bom-lifecycle-activator.component'; +import { + CountryFlagGeneratorComponent +} from '@shared/components/country-flag-generator/country-flag-generator.component'; +import {DateTimeComponent} from '@shared/components/dateTime/dateTime.component'; +import {FormErrorMessageComponent} from '@shared/components/formErrorMessage/formErrorMessage.component'; +import {InputComponent} from '@shared/components/input/input.component'; import { MultiSelectAutocompleteComponent } from '@shared/components/multi-select-autocomplete/multi-select-autocomplete.component'; @@ -35,48 +39,57 @@ import {NotificationOverviewComponent} from '@shared/components/notification-ove import {NotificationReasonComponent} from '@shared/components/notification-reason/notification-reason.component'; import {NotificationUserComponent} from '@shared/components/notification-user/notification-user.component'; import {PartsTableComponent} from '@shared/components/parts-table/parts-table.component'; -import {RequestInvestigationComponent } from '@shared/components/request-notification'; -import { RequestAlertComponent } from '@shared/components/request-notification/request-alert.component'; -import { SeveritySelectComponent } from '@shared/components/severity-select/severity-select.component'; -import { SeverityComponent } from '@shared/components/severity/severity.component'; -import { TableSettingsComponent } from '@shared/components/table-settings/table-settings.component'; -import { TextWithIconComponent } from '@shared/components/text-with-icon/text-with-icon.component'; -import { ViewSelectorComponent } from '@shared/components/view-selector/view-selector.component'; -import { NotificationModalContentComponent } from '@shared/modules/notification/modal/content/notification-modal-content.component'; -import { FlattenObjectPipe } from '@shared/pipes/flatten-object.pipe'; -import { FormatPaginationSemanticDataModelToCamelCasePipe } from '@shared/pipes/format-pagination-semantic-data-model-to-camelcase.pipe'; -import { FormatPartSemanticDataModelToCamelCasePipe } from '@shared/pipes/format-part-semantic-data-model-to-camelcase.pipe'; -import { FormatPartlistSemanticDataModelToCamelCasePipe } from '@shared/pipes/format-partlist-semantic-data-model-to-camelcase.pipe'; -import { I18NextModule } from 'angular-i18next'; -import { BaseInputComponent } from './abstraction/baseInput/baseInput.component'; -import { AvatarComponent } from './components/avatar/avatar.component'; -import { BreadcrumbsComponent } from './components/breadcrumbs/breadcrumbs.component'; -import { ButtonComponent } from './components/button/button.component'; -import { CardIconComponent } from './components/card-icon/card-icon.component'; -import { CardListComponent } from './components/card-list/card-list.component'; -import { ToKeyValuePipe } from './components/card-list/card-list.pipe'; -import { DataLoadingErrorComponent } from './components/data-loading-error/data-loading-error.component'; -import { LanguageSelectorComponent } from './components/language-selector/language-selector.component'; -import { PaginatorIntlService } from './components/pagination/paginator-intl.service'; -import { QualityTypeComponent } from './components/quality-type/quality-type.component'; -import { SelectComponent } from './components/select/select.component'; -import { ValueToLablePipe } from './components/select/valueToLable.pipe'; -import { SidenavWrapperComponent } from './components/sidenav/sidenav-wrapper.component'; -import { TableComponent } from './components/table/table.component'; -import { TextareaComponent } from './components/textarea/textarea.component'; -import { ToastContainerComponent } from './components/toasts/toast-container/toast-container.component'; -import { ToastMessageComponent } from './components/toasts/toast-message/toast-message.component'; -import { RoleDirective } from './directives/role.directive'; -import { TabAsPanelDirective } from './directives/tabs/tab-as-panel.directive'; -import { TooltipDirective } from './directives/tooltip.directive'; -import { ViewContainerDirective } from './directives/view-container.directive'; -import { AutoFormatPipe } from './pipes/auto-format.pipe'; -import { ErrorMessagePipe } from './pipes/error-message.pipe'; -import { FormatDatePipe } from './pipes/format-date.pipe'; -import { I18nPipe } from './pipes/i18n.pipe'; -import { PartsService } from './service/parts.service'; -import { StaticIdService } from './service/staticId.service'; -import { TemplateModule } from './template.module'; +import {RequestInvestigationComponent} from '@shared/components/request-notification'; +import {RequestAlertComponent} from '@shared/components/request-notification/request-alert.component'; +import {SeveritySelectComponent} from '@shared/components/severity-select/severity-select.component'; +import {SeverityComponent} from '@shared/components/severity/severity.component'; +import {TableSettingsComponent} from '@shared/components/table-settings/table-settings.component'; +import {TextWithIconComponent} from '@shared/components/text-with-icon/text-with-icon.component'; +import {ViewSelectorComponent} from '@shared/components/view-selector/view-selector.component'; +import { + NotificationModalContentComponent +} from '@shared/modules/notification/modal/content/notification-modal-content.component'; +import {FlattenObjectPipe} from '@shared/pipes/flatten-object.pipe'; +import { + FormatPaginationSemanticDataModelToCamelCasePipe +} from '@shared/pipes/format-pagination-semantic-data-model-to-camelcase.pipe'; +import { + FormatPartSemanticDataModelToCamelCasePipe +} from '@shared/pipes/format-part-semantic-data-model-to-camelcase.pipe'; +import { + FormatPartlistSemanticDataModelToCamelCasePipe +} from '@shared/pipes/format-partlist-semantic-data-model-to-camelcase.pipe'; +import {I18NextModule} from 'angular-i18next'; +import {BaseInputComponent} from './abstraction/baseInput/baseInput.component'; +import {AvatarComponent} from './components/avatar/avatar.component'; +import {BreadcrumbsComponent} from './components/breadcrumbs/breadcrumbs.component'; +import {ButtonComponent} from './components/button/button.component'; +import {CardIconComponent} from './components/card-icon/card-icon.component'; +import {CardListComponent} from './components/card-list/card-list.component'; +import {ToKeyValuePipe} from './components/card-list/card-list.pipe'; +import {DataLoadingErrorComponent} from './components/data-loading-error/data-loading-error.component'; +import {LanguageSelectorComponent} from './components/language-selector/language-selector.component'; +import {PaginatorIntlService} from './components/pagination/paginator-intl.service'; +import {QualityTypeComponent} from './components/quality-type/quality-type.component'; +import {SelectComponent} from './components/select/select.component'; +import {ValueToLablePipe} from './components/select/valueToLable.pipe'; +import {SidenavWrapperComponent} from './components/sidenav/sidenav-wrapper.component'; +import {TableComponent} from './components/table/table.component'; +import {TextareaComponent} from './components/textarea/textarea.component'; +import {ToastContainerComponent} from './components/toasts/toast-container/toast-container.component'; +import {ToastMessageComponent} from './components/toasts/toast-message/toast-message.component'; +import {RoleDirective} from './directives/role.directive'; +import {TabAsPanelDirective} from './directives/tabs/tab-as-panel.directive'; +import {TooltipDirective} from './directives/tooltip.directive'; +import {ViewContainerDirective} from './directives/view-container.directive'; +import {AutoFormatPipe} from './pipes/auto-format.pipe'; +import {ErrorMessagePipe} from './pipes/error-message.pipe'; +import {FormatDatePipe} from './pipes/format-date.pipe'; +import {I18nPipe} from './pipes/i18n.pipe'; +import {PartsService} from './service/parts.service'; +import {StaticIdService} from './service/staticId.service'; +import {TemplateModule} from './template.module'; +import {AbbreviateNumberPipe} from "@shared/pipes/abbreviate-number.pipe"; @NgModule({ declarations: [ @@ -127,6 +140,7 @@ import { TemplateModule } from './template.module'; MultiSelectAutocompleteComponent, CountryFlagGeneratorComponent, TableSettingsComponent, + AbbreviateNumberPipe, ], imports: [TemplateModule, RouterModule, I18NextModule], exports: [ @@ -175,6 +189,7 @@ import { TemplateModule } from './template.module'; PartsTableComponent, MultiSelectAutocompleteComponent, CountryFlagGeneratorComponent, + AbbreviateNumberPipe, ], providers: [ FormatDatePipe, From 9ccdf859a04e18e4b8be49d7d8053bb8aafdd876 Mon Sep 17 00:00:00 2001 From: Martin Maul Date: Tue, 21 Nov 2023 14:41:50 +0100 Subject: [PATCH 07/17] feature(UI):[TRACEFOSS-604] added correct abbreviation and number formatting for counts --- .../services/dashboard-mock/dashboard.model.ts | 4 ++-- .../shared/pipes/abbreviate-number.pipe.ts | 18 +++++++++++------- frontend/src/assets/locales/en/common.json | 2 +- 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/frontend/src/app/mocks/services/dashboard-mock/dashboard.model.ts b/frontend/src/app/mocks/services/dashboard-mock/dashboard.model.ts index b27e30b33e..eeaaeb9372 100644 --- a/frontend/src/app/mocks/services/dashboard-mock/dashboard.model.ts +++ b/frontend/src/app/mocks/services/dashboard-mock/dashboard.model.ts @@ -28,10 +28,10 @@ export const mockDashboardStats: DashboardStatsResponse = { otherPartsWithOpenAlerts: 3, otherPartsWithOpenInvestigations: 5, // part counts - asBuiltCustomerParts: 50, + asBuiltCustomerParts: 612, asPlannedCustomerParts: 5, asBuiltSupplierParts: 500, asPlannedSupplierParts: 5000, - asBuiltOwnParts: 120543, + asBuiltOwnParts: 20543, asPlannedOwnParts: 11203 }; diff --git a/frontend/src/app/modules/shared/pipes/abbreviate-number.pipe.ts b/frontend/src/app/modules/shared/pipes/abbreviate-number.pipe.ts index 4afae9a572..b1463fd5ba 100644 --- a/frontend/src/app/modules/shared/pipes/abbreviate-number.pipe.ts +++ b/frontend/src/app/modules/shared/pipes/abbreviate-number.pipe.ts @@ -4,9 +4,15 @@ import {Inject, LOCALE_ID, Pipe, PipeTransform} from "@angular/core"; export class AbbreviateNumberPipe implements PipeTransform { constructor(@Inject(LOCALE_ID) private locale: string) {} - transform(value: number | string): string { + transform(value: string): string { // Convert value to a number if it's a string - const numericValue = typeof value === 'string' ? parseFloat(value.replace(/,/g, '')) : value; + console.log("input is ", typeof value); + const numericValue = parseFloat(value.replace(/[,.]/g, '')); + console.log(this.locale) + + if(numericValue > 9999999) { + return 'Out of Range'; + } if (numericValue >= 1000000) { if (this.locale === 'de') { @@ -14,12 +20,10 @@ export class AbbreviateNumberPipe implements PipeTransform { } else if (this.locale === 'en') { return (numericValue / 1000000).toFixed(1) + ' M.'; } else { - return String(numericValue); + return value; } - } else if (numericValue > 9999999) { - return 'Out of Range'; - } else { - return String(numericValue); + } else { + return value; } } } diff --git a/frontend/src/assets/locales/en/common.json b/frontend/src/assets/locales/en/common.json index b60eb73e16..f95e8b64f1 100644 --- a/frontend/src/assets/locales/en/common.json +++ b/frontend/src/assets/locales/en/common.json @@ -229,7 +229,7 @@ "viewAll": "View all", "tabs": { "received": "Received", - "queuedAndRequested": "Queued & Sent" + "queuedAndRequested": "Queued & Requested" }, "status": { "SENT": "Sent", From a193809332b1719b7a1bb64653c6c3916af7bf82 Mon Sep 17 00:00:00 2001 From: Martin Maul Date: Wed, 22 Nov 2023 09:07:43 +0100 Subject: [PATCH 08/17] feature(UI):[TRACEFOSS-604] added styles --- .../presentation/dashboard.component.html | 3 +- .../presentation/dashboard.component.ts | 31 ++-- .../card-icon/card-icon.component.html | 11 +- .../card-icon/card-icon.component.scss | 11 +- .../card-icon/card-icon.component.ts | 3 + .../shared/pipes/abbreviate-number.pipe.ts | 11 +- .../src/app/modules/shared/shared.module.ts | 135 ++++++++---------- 7 files changed, 113 insertions(+), 92 deletions(-) diff --git a/frontend/src/app/modules/page/dashboard/presentation/dashboard.component.html b/frontend/src/app/modules/page/dashboard/presentation/dashboard.component.html index 69211a4249..5e139fca38 100644 --- a/frontend/src/app/modules/page/dashboard/presentation/dashboard.component.html +++ b/frontend/src/app/modules/page/dashboard/presentation/dashboard.component.html @@ -249,7 +249,8 @@

- + + diff --git a/frontend/src/app/modules/page/dashboard/presentation/dashboard.component.ts b/frontend/src/app/modules/page/dashboard/presentation/dashboard.component.ts index a0075acce7..125a54adef 100644 --- a/frontend/src/app/modules/page/dashboard/presentation/dashboard.component.ts +++ b/frontend/src/app/modules/page/dashboard/presentation/dashboard.component.ts @@ -19,21 +19,19 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ -import {Component, OnDestroy, OnInit, ViewChild} from '@angular/core'; -import {Router} from '@angular/router'; -import {ALERT_BASE_ROUTE, getRoute, INVESTIGATION_BASE_ROUTE} from '@core/known-route'; +import { Component, OnDestroy, OnInit, ViewChild } from '@angular/core'; +import { Router } from '@angular/router'; +import { ALERT_BASE_ROUTE, getRoute, INVESTIGATION_BASE_ROUTE } from '@core/known-route'; import { Notification, Notifications, NotificationStatusGroup, NotificationType, } from '@shared/model/notification.model'; -import {View} from '@shared/model/view.model'; -import { - CloseNotificationModalComponent -} from '@shared/modules/notification/modal/close/close-notification-modal.component'; -import {Observable} from 'rxjs'; -import {DashboardFacade} from '../abstraction/dashboard.facade'; +import { View } from '@shared/model/view.model'; +import { CloseNotificationModalComponent } from '@shared/modules/notification/modal/close/close-notification-modal.component'; +import { Observable } from 'rxjs'; +import { DashboardFacade } from '../abstraction/dashboard.facade'; @Component({ selector: 'app-dashboard', @@ -58,6 +56,8 @@ export class DashboardComponent implements OnInit, OnDestroy { public readonly alertLink: string; public readonly alertParams: Record; + public metricData: {metricLabelKey: string,value: Observable>,metricUnitLabelKey: string}[]; + constructor(private readonly dashboardFacade: DashboardFacade, private readonly router: Router) { this.numberOfTotalMyParts$ = this.dashboardFacade.numberOfTotalMyParts$; this.numberOfTotalOtherParts$ = this.dashboardFacade.numberOfTotalOtherParts$; @@ -76,7 +76,18 @@ export class DashboardComponent implements OnInit, OnDestroy { this.alertLink = alertLink; this.alertParams = alertQueryParams; - console.log(this.investigationsReceived$.subscribe((next) => console.log(next))); + this.metricData = [ + { + metricLabelKey: 'numberOfTotalMyParts', + value: this.numberOfTotalMyParts$, + metricUnitLabelKey: 'parts' + }, + { + metricLabelKey: 'numberOfTotalOtherParts', + value: this.numberOfTotalOtherParts$, + metricUnitLabelKey: 'parts' + }, + ] } public ngOnInit(): void { diff --git a/frontend/src/app/modules/shared/components/card-icon/card-icon.component.html b/frontend/src/app/modules/shared/components/card-icon/card-icon.component.html index 162becb7ee..d5e71c671f 100644 --- a/frontend/src/app/modules/shared/components/card-icon/card-icon.component.html +++ b/frontend/src/app/modules/shared/components/card-icon/card-icon.component.html @@ -32,7 +32,16 @@

{{ this.label }}

-

{{ this.stats }}

+
+ + + +
+ diff --git a/frontend/src/app/modules/shared/components/card-icon/card-icon.component.scss b/frontend/src/app/modules/shared/components/card-icon/card-icon.component.scss index 76b3135f5f..fe07ccc33d 100644 --- a/frontend/src/app/modules/shared/components/card-icon/card-icon.component.scss +++ b/frontend/src/app/modules/shared/components/card-icon/card-icon.component.scss @@ -24,11 +24,12 @@ } .card-container { - @apply flex flex-row gap-x-5 items-center; + @apply flex flex-row gap-x-5 items-start; } .avatar-container { - @apply flex flex-col items-center justify-center; + height: 100%; + @apply flex flex-col items-start justify-start; } .avatar { @@ -51,3 +52,9 @@ p:empty { width: 100px; height: 10px; } + +.card--icon--card--metrics--container { + display: flex; + gap: 10px; + flex-wrap: wrap; +} diff --git a/frontend/src/app/modules/shared/components/card-icon/card-icon.component.ts b/frontend/src/app/modules/shared/components/card-icon/card-icon.component.ts index 441f97b432..83e2eddb39 100644 --- a/frontend/src/app/modules/shared/components/card-icon/card-icon.component.ts +++ b/frontend/src/app/modules/shared/components/card-icon/card-icon.component.ts @@ -20,7 +20,9 @@ ********************************************************************************/ import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; +import { View } from '@shared/model/view.model'; import { StaticIdService } from '@shared/service/staticId.service'; +import { Observable } from 'rxjs'; @Component({ selector: 'app-card-icon', @@ -35,6 +37,7 @@ export class CardIconComponent { @Input() label: string; @Input() stats: number | string; @Input() icon: string; + @Input() metricData: {metricLabelKey: string, value: Observable>,metricUnitLabelKey: string}[]; constructor(staticIdService: StaticIdService) { this.htmlId = staticIdService.generateId(this.htmlIdBase); diff --git a/frontend/src/app/modules/shared/pipes/abbreviate-number.pipe.ts b/frontend/src/app/modules/shared/pipes/abbreviate-number.pipe.ts index b1463fd5ba..b98a2a6512 100644 --- a/frontend/src/app/modules/shared/pipes/abbreviate-number.pipe.ts +++ b/frontend/src/app/modules/shared/pipes/abbreviate-number.pipe.ts @@ -1,14 +1,15 @@ -import {Inject, LOCALE_ID, Pipe, PipeTransform} from "@angular/core"; +import { Inject, LOCALE_ID, Pipe, PipeTransform } from '@angular/core'; @Pipe({ name: 'abbreviateNumber', pure: false }) export class AbbreviateNumberPipe implements PipeTransform { constructor(@Inject(LOCALE_ID) private locale: string) {} - transform(value: string): string { - // Convert value to a number if it's a string - console.log("input is ", typeof value); + transform(value: string ): string { + if(!value) { + return; + } + // Convert value to a number f.ex. 1.000 to 1000 const numericValue = parseFloat(value.replace(/[,.]/g, '')); - console.log(this.locale) if(numericValue > 9999999) { return 'Out of Range'; diff --git a/frontend/src/app/modules/shared/shared.module.ts b/frontend/src/app/modules/shared/shared.module.ts index 2541004d20..25da647eb6 100644 --- a/frontend/src/app/modules/shared/shared.module.ts +++ b/frontend/src/app/modules/shared/shared.module.ts @@ -19,77 +19,64 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ -import {DatePipe, TitleCasePipe} from '@angular/common'; -import {NgModule} from '@angular/core'; -import {MatPaginatorIntl} from '@angular/material/paginator'; -import {RouterModule} from '@angular/router'; -import { - BomLifecycleActivatorComponent -} from '@shared/components/bom-lifecycle-activator/bom-lifecycle-activator.component'; -import { - CountryFlagGeneratorComponent -} from '@shared/components/country-flag-generator/country-flag-generator.component'; -import {DateTimeComponent} from '@shared/components/dateTime/dateTime.component'; -import {FormErrorMessageComponent} from '@shared/components/formErrorMessage/formErrorMessage.component'; -import {InputComponent} from '@shared/components/input/input.component'; -import { - MultiSelectAutocompleteComponent -} from '@shared/components/multi-select-autocomplete/multi-select-autocomplete.component'; -import {NotificationOverviewComponent} from '@shared/components/notification-overview/notification-overview.component'; -import {NotificationReasonComponent} from '@shared/components/notification-reason/notification-reason.component'; -import {NotificationUserComponent} from '@shared/components/notification-user/notification-user.component'; -import {PartsTableComponent} from '@shared/components/parts-table/parts-table.component'; -import {RequestInvestigationComponent} from '@shared/components/request-notification'; -import {RequestAlertComponent} from '@shared/components/request-notification/request-alert.component'; -import {SeveritySelectComponent} from '@shared/components/severity-select/severity-select.component'; -import {SeverityComponent} from '@shared/components/severity/severity.component'; -import {TableSettingsComponent} from '@shared/components/table-settings/table-settings.component'; -import {TextWithIconComponent} from '@shared/components/text-with-icon/text-with-icon.component'; -import {ViewSelectorComponent} from '@shared/components/view-selector/view-selector.component'; -import { - NotificationModalContentComponent -} from '@shared/modules/notification/modal/content/notification-modal-content.component'; -import {FlattenObjectPipe} from '@shared/pipes/flatten-object.pipe'; -import { - FormatPaginationSemanticDataModelToCamelCasePipe -} from '@shared/pipes/format-pagination-semantic-data-model-to-camelcase.pipe'; -import { - FormatPartSemanticDataModelToCamelCasePipe -} from '@shared/pipes/format-part-semantic-data-model-to-camelcase.pipe'; -import { - FormatPartlistSemanticDataModelToCamelCasePipe -} from '@shared/pipes/format-partlist-semantic-data-model-to-camelcase.pipe'; -import {I18NextModule} from 'angular-i18next'; -import {BaseInputComponent} from './abstraction/baseInput/baseInput.component'; -import {AvatarComponent} from './components/avatar/avatar.component'; -import {BreadcrumbsComponent} from './components/breadcrumbs/breadcrumbs.component'; -import {ButtonComponent} from './components/button/button.component'; -import {CardIconComponent} from './components/card-icon/card-icon.component'; -import {CardListComponent} from './components/card-list/card-list.component'; -import {ToKeyValuePipe} from './components/card-list/card-list.pipe'; -import {DataLoadingErrorComponent} from './components/data-loading-error/data-loading-error.component'; -import {LanguageSelectorComponent} from './components/language-selector/language-selector.component'; -import {PaginatorIntlService} from './components/pagination/paginator-intl.service'; -import {QualityTypeComponent} from './components/quality-type/quality-type.component'; -import {SelectComponent} from './components/select/select.component'; -import {ValueToLablePipe} from './components/select/valueToLable.pipe'; -import {SidenavWrapperComponent} from './components/sidenav/sidenav-wrapper.component'; -import {TableComponent} from './components/table/table.component'; -import {TextareaComponent} from './components/textarea/textarea.component'; -import {ToastContainerComponent} from './components/toasts/toast-container/toast-container.component'; -import {ToastMessageComponent} from './components/toasts/toast-message/toast-message.component'; -import {RoleDirective} from './directives/role.directive'; -import {TabAsPanelDirective} from './directives/tabs/tab-as-panel.directive'; -import {TooltipDirective} from './directives/tooltip.directive'; -import {ViewContainerDirective} from './directives/view-container.directive'; -import {AutoFormatPipe} from './pipes/auto-format.pipe'; -import {ErrorMessagePipe} from './pipes/error-message.pipe'; -import {FormatDatePipe} from './pipes/format-date.pipe'; -import {I18nPipe} from './pipes/i18n.pipe'; -import {PartsService} from './service/parts.service'; -import {StaticIdService} from './service/staticId.service'; -import {TemplateModule} from './template.module'; -import {AbbreviateNumberPipe} from "@shared/pipes/abbreviate-number.pipe"; +import { DatePipe, TitleCasePipe } from '@angular/common'; +import { NgModule } from '@angular/core'; +import { MatPaginatorIntl } from '@angular/material/paginator'; +import { RouterModule } from '@angular/router'; +import { BomLifecycleActivatorComponent } from '@shared/components/bom-lifecycle-activator/bom-lifecycle-activator.component'; +import { CardMetricComponent } from '@shared/components/card-metric/card-metric.component'; +import { CountryFlagGeneratorComponent } from '@shared/components/country-flag-generator/country-flag-generator.component'; +import { DateTimeComponent } from '@shared/components/dateTime/dateTime.component'; +import { FormErrorMessageComponent } from '@shared/components/formErrorMessage/formErrorMessage.component'; +import { InputComponent } from '@shared/components/input/input.component'; +import { MultiSelectAutocompleteComponent } from '@shared/components/multi-select-autocomplete/multi-select-autocomplete.component'; +import { NotificationOverviewComponent } from '@shared/components/notification-overview/notification-overview.component'; +import { NotificationReasonComponent } from '@shared/components/notification-reason/notification-reason.component'; +import { NotificationUserComponent } from '@shared/components/notification-user/notification-user.component'; +import { PartsTableComponent } from '@shared/components/parts-table/parts-table.component'; +import { RequestInvestigationComponent } from '@shared/components/request-notification'; +import { RequestAlertComponent } from '@shared/components/request-notification/request-alert.component'; +import { SeveritySelectComponent } from '@shared/components/severity-select/severity-select.component'; +import { SeverityComponent } from '@shared/components/severity/severity.component'; +import { TableSettingsComponent } from '@shared/components/table-settings/table-settings.component'; +import { TextWithIconComponent } from '@shared/components/text-with-icon/text-with-icon.component'; +import { ViewSelectorComponent } from '@shared/components/view-selector/view-selector.component'; +import { NotificationModalContentComponent } from '@shared/modules/notification/modal/content/notification-modal-content.component'; +import { AbbreviateNumberPipe } from '@shared/pipes/abbreviate-number.pipe'; +import { FlattenObjectPipe } from '@shared/pipes/flatten-object.pipe'; +import { FormatPaginationSemanticDataModelToCamelCasePipe } from '@shared/pipes/format-pagination-semantic-data-model-to-camelcase.pipe'; +import { FormatPartSemanticDataModelToCamelCasePipe } from '@shared/pipes/format-part-semantic-data-model-to-camelcase.pipe'; +import { FormatPartlistSemanticDataModelToCamelCasePipe } from '@shared/pipes/format-partlist-semantic-data-model-to-camelcase.pipe'; +import { I18NextModule } from 'angular-i18next'; +import { BaseInputComponent } from './abstraction/baseInput/baseInput.component'; +import { AvatarComponent } from './components/avatar/avatar.component'; +import { BreadcrumbsComponent } from './components/breadcrumbs/breadcrumbs.component'; +import { ButtonComponent } from './components/button/button.component'; +import { CardIconComponent } from './components/card-icon/card-icon.component'; +import { CardListComponent } from './components/card-list/card-list.component'; +import { ToKeyValuePipe } from './components/card-list/card-list.pipe'; +import { DataLoadingErrorComponent } from './components/data-loading-error/data-loading-error.component'; +import { LanguageSelectorComponent } from './components/language-selector/language-selector.component'; +import { PaginatorIntlService } from './components/pagination/paginator-intl.service'; +import { QualityTypeComponent } from './components/quality-type/quality-type.component'; +import { SelectComponent } from './components/select/select.component'; +import { ValueToLablePipe } from './components/select/valueToLable.pipe'; +import { SidenavWrapperComponent } from './components/sidenav/sidenav-wrapper.component'; +import { TableComponent } from './components/table/table.component'; +import { TextareaComponent } from './components/textarea/textarea.component'; +import { ToastContainerComponent } from './components/toasts/toast-container/toast-container.component'; +import { ToastMessageComponent } from './components/toasts/toast-message/toast-message.component'; +import { RoleDirective } from './directives/role.directive'; +import { TabAsPanelDirective } from './directives/tabs/tab-as-panel.directive'; +import { TooltipDirective } from './directives/tooltip.directive'; +import { ViewContainerDirective } from './directives/view-container.directive'; +import { AutoFormatPipe } from './pipes/auto-format.pipe'; +import { ErrorMessagePipe } from './pipes/error-message.pipe'; +import { FormatDatePipe } from './pipes/format-date.pipe'; +import { I18nPipe } from './pipes/i18n.pipe'; +import { PartsService } from './service/parts.service'; +import { StaticIdService } from './service/staticId.service'; +import { TemplateModule } from './template.module'; @NgModule({ declarations: [ @@ -139,9 +126,10 @@ import {AbbreviateNumberPipe} from "@shared/pipes/abbreviate-number.pipe"; ViewSelectorComponent, MultiSelectAutocompleteComponent, CountryFlagGeneratorComponent, - TableSettingsComponent, + TableSettingsComponent, AbbreviateNumberPipe, - ], + CardMetricComponent, + ], imports: [TemplateModule, RouterModule, I18NextModule], exports: [ ToastContainerComponent, @@ -190,6 +178,7 @@ import {AbbreviateNumberPipe} from "@shared/pipes/abbreviate-number.pipe"; MultiSelectAutocompleteComponent, CountryFlagGeneratorComponent, AbbreviateNumberPipe, + CardMetricComponent, ], providers: [ FormatDatePipe, From 8d8ab5710aec3b5fa7e8d39dc55a7bd42fd35a5c Mon Sep 17 00:00:00 2001 From: Martin Maul Date: Wed, 22 Nov 2023 17:05:53 +0100 Subject: [PATCH 09/17] feature(UI):[TRACEFOSS-604] added notification widgets --- .../dashboard-mock/dashboard.model.ts | 7 +- .../dashboard/abstraction/dashboard.facade.ts | 37 ++++++-- .../dashboard/core/dashboard.assembler.ts | 14 ++- .../page/dashboard/core/dashboard.state.ts | 47 +++++++++- .../page/dashboard/model/dashboard.model.ts | 19 ++-- .../presentation/dashboard.component.html | 32 +++++-- .../presentation/dashboard.component.scss | 4 +- .../presentation/dashboard.component.ts | 87 +++++++++++++++---- .../dashboard/presentation/dashboard.model.ts | 8 ++ .../card-icon/card-icon.component.html | 4 +- .../card-icon/card-icon.component.ts | 9 +- .../card-metric/card-metric.component.html | 11 +++ .../card-metric/card-metric.component.scss | 5 ++ .../card-metric/card-metric.component.spec.ts | 23 +++++ .../card-metric/card-metric.component.ts | 15 ++++ .../src/assets/locales/en/page.dashboard.json | 18 +++- 16 files changed, 281 insertions(+), 59 deletions(-) create mode 100644 frontend/src/app/modules/page/dashboard/presentation/dashboard.model.ts create mode 100644 frontend/src/app/modules/shared/components/card-metric/card-metric.component.html create mode 100644 frontend/src/app/modules/shared/components/card-metric/card-metric.component.scss create mode 100644 frontend/src/app/modules/shared/components/card-metric/card-metric.component.spec.ts create mode 100644 frontend/src/app/modules/shared/components/card-metric/card-metric.component.ts diff --git a/frontend/src/app/mocks/services/dashboard-mock/dashboard.model.ts b/frontend/src/app/mocks/services/dashboard-mock/dashboard.model.ts index eeaaeb9372..13e20e30d4 100644 --- a/frontend/src/app/mocks/services/dashboard-mock/dashboard.model.ts +++ b/frontend/src/app/mocks/services/dashboard-mock/dashboard.model.ts @@ -25,8 +25,11 @@ export const mockDashboardStats: DashboardStatsResponse = { // notification counts (where open means notficaiton status not closed) myPartsWithOpenAlerts: 10, myPartsWithOpenInvestigations: 8, - otherPartsWithOpenAlerts: 3, - otherPartsWithOpenInvestigations: 5, + supplierPartsWithOpenAlerts: 3, + customerPartsWithOpenAlerts: 2, + supplierPartsWithOpenInvestigations: 6, + customerPartsWithOpenInvestigations: 12, + // part counts asBuiltCustomerParts: 612, asPlannedCustomerParts: 5, diff --git a/frontend/src/app/modules/page/dashboard/abstraction/dashboard.facade.ts b/frontend/src/app/modules/page/dashboard/abstraction/dashboard.facade.ts index ef049bcbaf..765bbf817a 100644 --- a/frontend/src/app/modules/page/dashboard/abstraction/dashboard.facade.ts +++ b/frontend/src/app/modules/page/dashboard/abstraction/dashboard.facade.ts @@ -75,6 +75,19 @@ export class DashboardFacade { return this.dashboardState.recentCreatedAlerts$; } + public get numberOfOwnOpenInvestigationsReceived$(): Observable> { + return this.dashboardState.numberOfOwnOpenInvestigationsReceived$; + } + public get numberOfOwnOpenInvestigationsCreated$(): Observable> { + return this.dashboardState.numberOfOwnOpenInvestigationsCreated$; + } + public get numberOfOwnOpenAlertsReceived$(): Observable> { + return this.dashboardState.numberOfOwnOpenAlertsReceived$; + } + public get numberOfOwnOpenAlertsCreated$(): Observable> { + return this.dashboardState.numberOfOwnOpenAlertsCreated$; + } + public setDashboardData(): void { this.setAssetNumbers(); this.setReceivedInvestigations(); @@ -94,10 +107,11 @@ export class DashboardFacade { this.dashboardState.setNumberOfAsBuiltCustomerParts({loader: true}); this.dashboardState.setNumberOfAsPlannedCustomerParts({loader: true}); - this.dashboardState.setNumberOfMyPartsWithOpenInvestigations({loader: true}); - this.dashboardState.setNumberOfMyPartsWithOpenAlerts({loader:true}); - this.dashboardState.setNumberOfOtherPartsWithOpenInvestigations({loader:true}); - this.dashboardState.setNumberOfOtherPartsWithOpenAlerts({loader:true}); + this.dashboardState.setNumberOfOwnOpenInvestigationsReceived({loader:true}) + this.dashboardState.setNumberOfOwnOpenAlertsReceived({loader:true}) + this.dashboardState.setNumberOfOwnOpenInvestigationsCreated({loader:true}) + this.dashboardState.setNumberOfOwnOpenAlertsCreated({loader:true}) + this.assetNumbersSubscription?.unsubscribe(); this.assetNumbersSubscription = this.dashboardService.getStats().subscribe({ @@ -112,10 +126,12 @@ export class DashboardFacade { this.dashboardState.setNumberOfAsBuiltCustomerParts({data: dashboardStats.asBuiltCustomerParts}); this.dashboardState.setNumberOfAsPlannedCustomerParts({data: dashboardStats.asPlannedCustomerParts}); - this.dashboardState.setNumberOfMyPartsWithOpenInvestigations({data: dashboardStats.myPartsWithOpenInvestigations}); - this.dashboardState.setNumberOfMyPartsWithOpenAlerts({data: dashboardStats.myPartsWithOpenAlerts}); - this.dashboardState.setNumberOfOtherPartsWithOpenInvestigations({data: dashboardStats.otherPartsWithOpenInvestigations}); - this.dashboardState.setNumberOfOtherPartsWithOpenAlerts({data: dashboardStats.otherPartsWithOpenAlerts}); + this.dashboardState.setNumberOfOwnOpenInvestigationsReceived({data: dashboardStats.ownOpenInvestigationsReceived}) + this.dashboardState.setNumberOfOwnOpenAlertsReceived({data: dashboardStats.ownOpenAlertsReceived}) + this.dashboardState.setNumberOfOwnOpenInvestigationsCreated({data: dashboardStats.ownOpenInvestigationsCreated}) + this.dashboardState.setNumberOfOwnOpenAlertsCreated({data: dashboardStats.ownOpenAlertsCreated}) + + }, error: error => { this.dashboardState.setNumberOfTotalMyParts({ error }); @@ -132,6 +148,11 @@ export class DashboardFacade { this.dashboardState.setNumberOfMyPartsWithOpenAlerts({error}); this.dashboardState.setNumberOfOtherPartsWithOpenInvestigations({error}); this.dashboardState.setNumberOfOtherPartsWithOpenAlerts({error}); + + this.dashboardState.setNumberOfOwnOpenInvestigationsReceived({error}) + this.dashboardState.setNumberOfOwnOpenAlertsReceived({error}) + this.dashboardState.setNumberOfOwnOpenInvestigationsCreated({error}) + this.dashboardState.setNumberOfOwnOpenAlertsCreated({error}) }, }); } diff --git a/frontend/src/app/modules/page/dashboard/core/dashboard.assembler.ts b/frontend/src/app/modules/page/dashboard/core/dashboard.assembler.ts index 16ec8e063b..fe74c880b8 100644 --- a/frontend/src/app/modules/page/dashboard/core/dashboard.assembler.ts +++ b/frontend/src/app/modules/page/dashboard/core/dashboard.assembler.ts @@ -19,16 +19,15 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ -import { DashboardStats, DashboardStatsResponse } from '../model/dashboard.model'; +import {DashboardStats, DashboardStatsResponse} from '../model/dashboard.model'; export class DashboardAssembler { public static assembleDashboard(dashboard: DashboardStatsResponse): DashboardStats { return { - // notification counts (where open means notficaiton status not closed) + // notification counts (where open means notfication status not closed) myPartsWithOpenAlerts: dashboard.myPartsWithOpenAlerts, myPartsWithOpenInvestigations: dashboard.myPartsWithOpenInvestigations, - otherPartsWithOpenAlerts: dashboard.otherPartsWithOpenAlerts, - otherPartsWithOpenInvestigations: dashboard.otherPartsWithOpenInvestigations, + // part counts asBuiltCustomerParts: dashboard.asBuiltCustomerParts, asPlannedCustomerParts: dashboard.asPlannedCustomerParts, @@ -36,8 +35,15 @@ export class DashboardAssembler { asPlannedSupplierParts: dashboard.asPlannedSupplierParts, asBuiltOwnParts: dashboard.asBuiltOwnParts, asPlannedOwnParts: dashboard.asPlannedOwnParts, + + // calculated totalOwnParts: dashboard.asBuiltOwnParts + dashboard.asPlannedOwnParts, totalOtherParts: dashboard.asBuiltSupplierParts + dashboard.asBuiltCustomerParts, + ownOpenInvestigationsReceived: dashboard.myPartsWithOpenInvestigations, + ownOpenInvestigationsCreated: dashboard.supplierPartsWithOpenInvestigations + dashboard.customerPartsWithOpenInvestigations, + ownOpenAlertsReceived: dashboard.supplierPartsWithOpenAlerts + dashboard.customerPartsWithOpenAlerts, + ownOpenAlertsCreated: dashboard.myPartsWithOpenAlerts, + }; } } diff --git a/frontend/src/app/modules/page/dashboard/core/dashboard.state.ts b/frontend/src/app/modules/page/dashboard/core/dashboard.state.ts index 01f1136ecc..3f80cf1146 100644 --- a/frontend/src/app/modules/page/dashboard/core/dashboard.state.ts +++ b/frontend/src/app/modules/page/dashboard/core/dashboard.state.ts @@ -47,8 +47,16 @@ export class DashboardState { private readonly _numberOfOtherPartsWithOpenAlerts$: State> = new State>({ loader: true }); private readonly _numberOfOtherPartsWithOpenInvestigations$: State> = new State>({ loader: true }); + // calculated notification counts + private readonly _numberOfOwnOpenInvestigationsReceived$: State> = new State>({ loader: true }); + private readonly _numberOfOwnOpenInvestigationsCreated$: State> = new State>({ loader: true }); + private readonly _numberOfOwnOpenAlertsReceived$: State> = new State>({ loader: true }); + private readonly _numberOfOwnOpenAlertsCreated$: State> = new State>({ loader: true }); - // notifications received + + + + // recent notifications private readonly _recentReceivedInvestigations$: State> = new State>({ loader: true }); private readonly _recentCreatedInvestigations$: State> = new State>({ loader: true }); @@ -161,6 +169,43 @@ export class DashboardState { this._numberOfOtherPartsWithOpenInvestigations$.update(count); } + /** + * calculated notifications getter/setter + */ + + public get numberOfOwnOpenInvestigationsReceived$(): Observable> { + return this._numberOfOwnOpenInvestigationsReceived$.observable; + } + + public setNumberOfOwnOpenInvestigationsReceived(count: View): void { + this._numberOfOwnOpenInvestigationsReceived$.update(count); + } + + public get numberOfOwnOpenInvestigationsCreated$(): Observable> { + return this._numberOfOwnOpenInvestigationsCreated$.observable; + } + + public setNumberOfOwnOpenInvestigationsCreated(count: View): void { + this._numberOfOwnOpenInvestigationsCreated$.update(count); + } + + public get numberOfOwnOpenAlertsReceived$(): Observable> { + return this._numberOfOwnOpenAlertsReceived$.observable; + } + + public setNumberOfOwnOpenAlertsReceived(count: View): void { + this._numberOfOwnOpenAlertsReceived$.update(count); + } + + public get numberOfOwnOpenAlertsCreated$(): Observable> { + return this._numberOfOwnOpenAlertsCreated$.observable; + } + + public setNumberOfOwnOpenAlertsCreated(count: View): void { + this._numberOfOwnOpenAlertsCreated$.update(count); + } + + /** * recent notifications getter/setter */ diff --git a/frontend/src/app/modules/page/dashboard/model/dashboard.model.ts b/frontend/src/app/modules/page/dashboard/model/dashboard.model.ts index 96695194b7..a95a635f47 100644 --- a/frontend/src/app/modules/page/dashboard/model/dashboard.model.ts +++ b/frontend/src/app/modules/page/dashboard/model/dashboard.model.ts @@ -23,8 +23,7 @@ export interface DashboardStats { // notification counts (where open means notficaiton status not closed) myPartsWithOpenAlerts: number, myPartsWithOpenInvestigations: number, - otherPartsWithOpenAlerts: number, - otherPartsWithOpenInvestigations: number, + // part counts asBuiltCustomerParts: number, asPlannedCustomerParts: number, @@ -34,16 +33,24 @@ export interface DashboardStats { asPlannedOwnParts: number // calculated counts - totalOwnParts: number - totalOtherParts: number + totalOwnParts: number, + totalOtherParts: number, + ownOpenInvestigationsReceived: number, + ownOpenInvestigationsCreated: number, + ownOpenAlertsReceived: number, + ownOpenAlertsCreated: number + } export interface DashboardStatsResponse { // notification counts (where open means notficaiton status not closed) myPartsWithOpenAlerts: number, myPartsWithOpenInvestigations: number, - otherPartsWithOpenAlerts: number, - otherPartsWithOpenInvestigations: number, + supplierPartsWithOpenInvestigations: number, + customerPartsWithOpenInvestigations: number, + supplierPartsWithOpenAlerts: number, + customerPartsWithOpenAlerts: number, + // part counts asBuiltCustomerParts: number, asPlannedCustomerParts: number, diff --git a/frontend/src/app/modules/page/dashboard/presentation/dashboard.component.html b/frontend/src/app/modules/page/dashboard/presentation/dashboard.component.html index 5e139fca38..7e335e8fac 100644 --- a/frontend/src/app/modules/page/dashboard/presentation/dashboard.component.html +++ b/frontend/src/app/modules/page/dashboard/presentation/dashboard.component.html @@ -20,40 +20,53 @@ -->
-
+
-
+
-
+
+ +
+ +
+ @@ -249,7 +262,10 @@

- + diff --git a/frontend/src/app/modules/page/dashboard/presentation/dashboard.component.scss b/frontend/src/app/modules/page/dashboard/presentation/dashboard.component.scss index 7c4b0cd6c3..cde7aca148 100644 --- a/frontend/src/app/modules/page/dashboard/presentation/dashboard.component.scss +++ b/frontend/src/app/modules/page/dashboard/presentation/dashboard.component.scss @@ -23,8 +23,8 @@ @apply grid grid-cols-1 lg:grid-cols-12 gap-6 mt-6; } -.parts-section { - @apply col-span-12 lg:col-span-4; +.metrics-section { + @apply col-span-12 lg:col-span-3; } .dashboard--investigation { diff --git a/frontend/src/app/modules/page/dashboard/presentation/dashboard.component.ts b/frontend/src/app/modules/page/dashboard/presentation/dashboard.component.ts index 125a54adef..dea45e0ded 100644 --- a/frontend/src/app/modules/page/dashboard/presentation/dashboard.component.ts +++ b/frontend/src/app/modules/page/dashboard/presentation/dashboard.component.ts @@ -19,19 +19,22 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ -import { Component, OnDestroy, OnInit, ViewChild } from '@angular/core'; -import { Router } from '@angular/router'; -import { ALERT_BASE_ROUTE, getRoute, INVESTIGATION_BASE_ROUTE } from '@core/known-route'; +import {Component, OnDestroy, OnInit, ViewChild} from '@angular/core'; +import {Router} from '@angular/router'; +import {ALERT_BASE_ROUTE, getRoute, INVESTIGATION_BASE_ROUTE} from '@core/known-route'; import { Notification, Notifications, NotificationStatusGroup, NotificationType, } from '@shared/model/notification.model'; -import { View } from '@shared/model/view.model'; -import { CloseNotificationModalComponent } from '@shared/modules/notification/modal/close/close-notification-modal.component'; -import { Observable } from 'rxjs'; -import { DashboardFacade } from '../abstraction/dashboard.facade'; +import {View} from '@shared/model/view.model'; +import { + CloseNotificationModalComponent +} from '@shared/modules/notification/modal/close/close-notification-modal.component'; +import {Observable} from 'rxjs'; +import {DashboardFacade} from '../abstraction/dashboard.facade'; +import {MetricData} from "@page/dashboard/presentation/dashboard.model"; @Component({ selector: 'app-dashboard', @@ -45,6 +48,11 @@ export class DashboardComponent implements OnInit, OnDestroy { public readonly numberOfTotalOtherParts$: Observable>; public readonly numberOfInvestigations$: Observable>; + public readonly numberOfOwnInvestigationsReceived$: Observable>; + public readonly numberOfOwnInvestigationsCreated$: Observable>; + public readonly numberOfOwnAlertsReceived$: Observable>; + public readonly numberOfOwnAlertsCreated$: Observable>; + public readonly investigationsReceived$: Observable>; public readonly investigationsCreated$: Observable>; public readonly alertsReceived$: Observable>; @@ -56,7 +64,13 @@ export class DashboardComponent implements OnInit, OnDestroy { public readonly alertLink: string; public readonly alertParams: Record; - public metricData: {metricLabelKey: string,value: Observable>,metricUnitLabelKey: string}[]; + public partsMetricData: MetricData[]; + public otherPartsMetricData: MetricData[]; + public investigationsMetricData: MetricData[]; + public investigationsCreatedMetricData: MetricData[]; + public alertsMetricData: MetricData[]; + public alertsCreatedMetricData: MetricData[]; + constructor(private readonly dashboardFacade: DashboardFacade, private readonly router: Router) { this.numberOfTotalMyParts$ = this.dashboardFacade.numberOfTotalMyParts$; @@ -64,30 +78,69 @@ export class DashboardComponent implements OnInit, OnDestroy { this.numberOfInvestigations$ = this.dashboardFacade.numberOfMyPartsWithOpenInvestigations$; + this.numberOfOwnInvestigationsReceived$ = this.dashboardFacade.numberOfOwnOpenInvestigationsReceived$; + this.numberOfOwnInvestigationsCreated$ = this.dashboardFacade.numberOfOwnOpenInvestigationsCreated$; + this.numberOfOwnAlertsReceived$ = this.dashboardFacade.numberOfOwnOpenAlertsReceived$; + this.numberOfOwnAlertsCreated$ = this.dashboardFacade.numberOfOwnOpenAlertsCreated$; + + this.investigationsReceived$ = this.dashboardFacade.recentReceivedInvestigations$; this.investigationsCreated$ = this.dashboardFacade.recentCreatedInvestigations$; this.alertsReceived$ = this.dashboardFacade.recentReceivedAlerts$ this.alertsCreated$ = this.dashboardFacade.recentCreatedAlerts$ - const { link: investigationLink, queryParams: investigationQueryParams } = getRoute(INVESTIGATION_BASE_ROUTE, NotificationStatusGroup.RECEIVED); - const { link: alertLink, queryParams: alertQueryParams } = getRoute(ALERT_BASE_ROUTE, NotificationStatusGroup.RECEIVED); + const {link: investigationLink, queryParams: investigationQueryParams} = getRoute(INVESTIGATION_BASE_ROUTE, NotificationStatusGroup.RECEIVED); + const {link: alertLink, queryParams: alertQueryParams} = getRoute(ALERT_BASE_ROUTE, NotificationStatusGroup.RECEIVED); this.investigationLink = investigationLink; this.investigationParams = investigationQueryParams; this.alertLink = alertLink; this.alertParams = alertQueryParams; - this.metricData = [ + this.partsMetricData = [ { - metricLabelKey: 'numberOfTotalMyParts', + metricName: 'totalAmount', value: this.numberOfTotalMyParts$, - metricUnitLabelKey: 'parts' - }, + metricUnit: 'parts' + } + + ] + + this.otherPartsMetricData = [ { - metricLabelKey: 'numberOfTotalOtherParts', + metricName: 'totalAmount', value: this.numberOfTotalOtherParts$, - metricUnitLabelKey: 'parts' + metricUnit: 'parts' + } + ]; + + this.investigationsMetricData = [ + { + metricName: 'amountReceived', + value: this.numberOfOwnInvestigationsReceived$, + metricUnit: 'investigations' }, - ] + { + metricName: 'amountCreated', + value: this.numberOfOwnInvestigationsCreated$, + metricUnit: 'investigations' + } + ]; + + + this.alertsMetricData = [ + { + metricName: 'amountReceived', + value: this.numberOfOwnAlertsReceived$, + metricUnit: 'alerts' + }, + { + metricName: 'amountCreated', + value: this.numberOfOwnAlertsCreated$, + metricUnit: 'alerts' + } + ]; + + } public ngOnInit(): void { diff --git a/frontend/src/app/modules/page/dashboard/presentation/dashboard.model.ts b/frontend/src/app/modules/page/dashboard/presentation/dashboard.model.ts new file mode 100644 index 0000000000..e8435ab407 --- /dev/null +++ b/frontend/src/app/modules/page/dashboard/presentation/dashboard.model.ts @@ -0,0 +1,8 @@ +import {Observable} from "rxjs"; +import {View} from "@shared/model/view.model"; + +export interface MetricData { + metricName: string, + value: Observable>, + metricUnit: string, +} diff --git a/frontend/src/app/modules/shared/components/card-icon/card-icon.component.html b/frontend/src/app/modules/shared/components/card-icon/card-icon.component.html index d5e71c671f..c8676609b8 100644 --- a/frontend/src/app/modules/shared/components/card-icon/card-icon.component.html +++ b/frontend/src/app/modules/shared/components/card-icon/card-icon.component.html @@ -35,9 +35,9 @@
diff --git a/frontend/src/app/modules/shared/components/card-icon/card-icon.component.ts b/frontend/src/app/modules/shared/components/card-icon/card-icon.component.ts index 83e2eddb39..9b31ac0c88 100644 --- a/frontend/src/app/modules/shared/components/card-icon/card-icon.component.ts +++ b/frontend/src/app/modules/shared/components/card-icon/card-icon.component.ts @@ -19,10 +19,9 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ -import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; -import { View } from '@shared/model/view.model'; -import { StaticIdService } from '@shared/service/staticId.service'; -import { Observable } from 'rxjs'; +import {ChangeDetectionStrategy, Component, Input} from '@angular/core'; +import {StaticIdService} from '@shared/service/staticId.service'; +import {MetricData} from "@page/dashboard/presentation/dashboard.model"; @Component({ selector: 'app-card-icon', @@ -37,7 +36,7 @@ export class CardIconComponent { @Input() label: string; @Input() stats: number | string; @Input() icon: string; - @Input() metricData: {metricLabelKey: string, value: Observable>,metricUnitLabelKey: string}[]; + @Input() metricData: MetricData[]; constructor(staticIdService: StaticIdService) { this.htmlId = staticIdService.generateId(this.htmlIdBase); diff --git a/frontend/src/app/modules/shared/components/card-metric/card-metric.component.html b/frontend/src/app/modules/shared/components/card-metric/card-metric.component.html new file mode 100644 index 0000000000..800cd74850 --- /dev/null +++ b/frontend/src/app/modules/shared/components/card-metric/card-metric.component.html @@ -0,0 +1,11 @@ + + +

{{headerLabelKey}}

+
+ +

{{((value | async)?.data | number) | abbreviateNumber}}

+
+ +

{{footerLabelKey}}

+
+
diff --git a/frontend/src/app/modules/shared/components/card-metric/card-metric.component.scss b/frontend/src/app/modules/shared/components/card-metric/card-metric.component.scss new file mode 100644 index 0000000000..6efe7e06d4 --- /dev/null +++ b/frontend/src/app/modules/shared/components/card-metric/card-metric.component.scss @@ -0,0 +1,5 @@ +.metric-card-container { + width: 140px; + height: 100px; + padding: 8px; +} diff --git a/frontend/src/app/modules/shared/components/card-metric/card-metric.component.spec.ts b/frontend/src/app/modules/shared/components/card-metric/card-metric.component.spec.ts new file mode 100644 index 0000000000..876bde513c --- /dev/null +++ b/frontend/src/app/modules/shared/components/card-metric/card-metric.component.spec.ts @@ -0,0 +1,23 @@ +import {ComponentFixture, TestBed} from '@angular/core/testing'; + +import {CardMetricComponent} from './card-metric.component'; + +describe('CardMetricComponent', () => { + let component: CardMetricComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ CardMetricComponent ] + }) + .compileComponents(); + + fixture = TestBed.createComponent(CardMetricComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/frontend/src/app/modules/shared/components/card-metric/card-metric.component.ts b/frontend/src/app/modules/shared/components/card-metric/card-metric.component.ts new file mode 100644 index 0000000000..5c04c8aff5 --- /dev/null +++ b/frontend/src/app/modules/shared/components/card-metric/card-metric.component.ts @@ -0,0 +1,15 @@ +import {Component, Input} from '@angular/core'; +import {Observable} from "rxjs"; +import {View} from "@shared/model/view.model"; + +@Component({ + selector: 'app-card-metric', + templateUrl: './card-metric.component.html', + styleUrls: ['./card-metric.component.scss'] +}) +export class CardMetricComponent { + @Input() headerLabelKey: string; + @Input() value: Observable>; + @Input() footerLabelKey: string; + +} diff --git a/frontend/src/assets/locales/en/page.dashboard.json b/frontend/src/assets/locales/en/page.dashboard.json index bf4bad44ef..7abdb40b06 100644 --- a/frontend/src/assets/locales/en/page.dashboard.json +++ b/frontend/src/assets/locales/en/page.dashboard.json @@ -1,13 +1,23 @@ { "pageDashboard": { "totalInvestigations": { - "label": "Total of open investigations" + "label": "Investigations" + }, + "totalAlerts": { + "label": "Alerts" }, "totalOfParts": { - "label": "Total of parts" + "label": "Parts" }, "totalOfOtherParts": { - "label": "Total of other parts" - } + "label": "Other Parts" + }, + "totalAmount": "Total Amount", + "parts": "Parts", + "investigations": "Investigations", + "alerts": "Alerts", + "amountReceived": "Amount Received", + "amountCreated": "Amount Created" + } } From 8bf8e1a7bd91785c160477881af22541d4661808 Mon Sep 17 00:00:00 2001 From: Martin Maul Date: Thu, 23 Nov 2023 10:33:23 +0100 Subject: [PATCH 10/17] feature(UI):[TRACEFOSS-604] added labeling --- .../dashboard-mock/dashboard.model.ts | 20 +++++++++---------- .../dashboard/core/dashboard.assembler.ts | 4 ++-- .../card-icon/card-icon.component.scss | 1 + .../card-metric/card-metric.component.html | 2 +- .../card-metric/card-metric.component.scss | 7 +++++-- .../src/assets/locales/de/page.dashboard.json | 17 ++++++++++++---- 6 files changed, 32 insertions(+), 19 deletions(-) diff --git a/frontend/src/app/mocks/services/dashboard-mock/dashboard.model.ts b/frontend/src/app/mocks/services/dashboard-mock/dashboard.model.ts index 13e20e30d4..e1fa20dd51 100644 --- a/frontend/src/app/mocks/services/dashboard-mock/dashboard.model.ts +++ b/frontend/src/app/mocks/services/dashboard-mock/dashboard.model.ts @@ -19,22 +19,22 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ -import {DashboardStatsResponse} from '@page/dashboard/model/dashboard.model'; +import { DashboardStatsResponse } from '@page/dashboard/model/dashboard.model'; export const mockDashboardStats: DashboardStatsResponse = { // notification counts (where open means notficaiton status not closed) - myPartsWithOpenAlerts: 10, - myPartsWithOpenInvestigations: 8, + myPartsWithOpenAlerts: 82, + myPartsWithOpenInvestigations: 33543, supplierPartsWithOpenAlerts: 3, - customerPartsWithOpenAlerts: 2, - supplierPartsWithOpenInvestigations: 6, + customerPartsWithOpenAlerts: 563, + supplierPartsWithOpenInvestigations: 4643, customerPartsWithOpenInvestigations: 12, // part counts - asBuiltCustomerParts: 612, - asPlannedCustomerParts: 5, - asBuiltSupplierParts: 500, - asPlannedSupplierParts: 5000, - asBuiltOwnParts: 20543, + asBuiltCustomerParts: 100, + asPlannedCustomerParts: 50, + asBuiltSupplierParts: 163000, + asPlannedSupplierParts: 2563, + asBuiltOwnParts: 5300000, asPlannedOwnParts: 11203 }; diff --git a/frontend/src/app/modules/page/dashboard/core/dashboard.assembler.ts b/frontend/src/app/modules/page/dashboard/core/dashboard.assembler.ts index fe74c880b8..92224bd407 100644 --- a/frontend/src/app/modules/page/dashboard/core/dashboard.assembler.ts +++ b/frontend/src/app/modules/page/dashboard/core/dashboard.assembler.ts @@ -19,7 +19,7 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ -import {DashboardStats, DashboardStatsResponse} from '../model/dashboard.model'; +import { DashboardStats, DashboardStatsResponse } from '../model/dashboard.model'; export class DashboardAssembler { public static assembleDashboard(dashboard: DashboardStatsResponse): DashboardStats { @@ -38,7 +38,7 @@ export class DashboardAssembler { // calculated totalOwnParts: dashboard.asBuiltOwnParts + dashboard.asPlannedOwnParts, - totalOtherParts: dashboard.asBuiltSupplierParts + dashboard.asBuiltCustomerParts, + totalOtherParts: dashboard.asBuiltSupplierParts + dashboard.asBuiltCustomerParts + dashboard.asPlannedSupplierParts + dashboard.asPlannedSupplierParts, ownOpenInvestigationsReceived: dashboard.myPartsWithOpenInvestigations, ownOpenInvestigationsCreated: dashboard.supplierPartsWithOpenInvestigations + dashboard.customerPartsWithOpenInvestigations, ownOpenAlertsReceived: dashboard.supplierPartsWithOpenAlerts + dashboard.customerPartsWithOpenAlerts, diff --git a/frontend/src/app/modules/shared/components/card-icon/card-icon.component.scss b/frontend/src/app/modules/shared/components/card-icon/card-icon.component.scss index fe07ccc33d..1203549e42 100644 --- a/frontend/src/app/modules/shared/components/card-icon/card-icon.component.scss +++ b/frontend/src/app/modules/shared/components/card-icon/card-icon.component.scss @@ -57,4 +57,5 @@ p:empty { display: flex; gap: 10px; flex-wrap: wrap; + margin-top: 3px; } diff --git a/frontend/src/app/modules/shared/components/card-metric/card-metric.component.html b/frontend/src/app/modules/shared/components/card-metric/card-metric.component.html index 800cd74850..a0e15757a0 100644 --- a/frontend/src/app/modules/shared/components/card-metric/card-metric.component.html +++ b/frontend/src/app/modules/shared/components/card-metric/card-metric.component.html @@ -6,6 +6,6 @@

{{((value | async)?.data | number) | abbreviateNumber}}

-

{{footerLabelKey}}

+

{{footerLabelKey}}

diff --git a/frontend/src/app/modules/shared/components/card-metric/card-metric.component.scss b/frontend/src/app/modules/shared/components/card-metric/card-metric.component.scss index 6efe7e06d4..d1b9af7540 100644 --- a/frontend/src/app/modules/shared/components/card-metric/card-metric.component.scss +++ b/frontend/src/app/modules/shared/components/card-metric/card-metric.component.scss @@ -1,5 +1,8 @@ .metric-card-container { - width: 140px; - height: 100px; + display: flex; + flex-direction: column; + justify-content: space-between; + width: 160px; + height: 110px; padding: 8px; } diff --git a/frontend/src/assets/locales/de/page.dashboard.json b/frontend/src/assets/locales/de/page.dashboard.json index c814043f11..c333a84dd5 100644 --- a/frontend/src/assets/locales/de/page.dashboard.json +++ b/frontend/src/assets/locales/de/page.dashboard.json @@ -1,13 +1,22 @@ { "pageDashboard": { "totalInvestigations": { - "label": "Anzahl offener Untersuchungen" + "label": "Qualitätsuntersuchungen" + }, + "totalAlerts": { + "label": "Qualitätswarnungen" }, "totalOfParts": { - "label": "Anzahl Produkte" + "label": "Produkte" }, "totalOfOtherParts": { - "label": "Anzahl anderer Produkte" - } + "label": "Andere Produkte" + }, + "totalAmount": "Gesamtanzahl", + "parts": "Produkte", + "investigations": "Untersuchungen", + "alerts": "Warnungen", + "amountReceived": "Anzahl zugewiesen", + "amountCreated": "Anzahl erstellt" } } From 0c96784e4052d008a809e51d806fb7e195b8ab37 Mon Sep 17 00:00:00 2001 From: Martin Maul Date: Thu, 23 Nov 2023 10:36:19 +0100 Subject: [PATCH 11/17] feature(UI):[TRACEFOSS-604] added labeling --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 91e3ac96dc..30fdb4b86b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ 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) From b0dc500a721addc5eeed8ec90ed896672480aa9f Mon Sep 17 00:00:00 2001 From: Martin Maul Date: Thu, 23 Nov 2023 14:09:32 +0100 Subject: [PATCH 12/17] feature(UI):[TRACEFOSS-604] remove unit --- .../presentation/customer-parts/customer-parts.component.html | 2 ++ .../presentation/supplier-parts/supplier-parts.component.html | 2 ++ .../shared/components/card-metric/card-metric.component.html | 2 ++ 3 files changed, 6 insertions(+) diff --git a/frontend/src/app/modules/page/other-parts/presentation/customer-parts/customer-parts.component.html b/frontend/src/app/modules/page/other-parts/presentation/customer-parts/customer-parts.component.html index ddabac0f7a..c71275fd18 100644 --- a/frontend/src/app/modules/page/other-parts/presentation/customer-parts/customer-parts.component.html +++ b/frontend/src/app/modules/page/other-parts/presentation/customer-parts/customer-parts.component.html @@ -39,6 +39,7 @@ [tableHeader]='"page.asBuiltParts" | i18n' (filterActivated)="filterActivated(true, $event )" [tableType]="PartTableType.AS_BUILT_CUSTOMER" + [mainAspectType]="MainAspectType.AS_BUILT" >
@@ -66,6 +67,7 @@ [multiSelectActive]="true" (filterActivated)="filterActivated(false, $event )" [tableType]="PartTableType.AS_PLANNED_CUSTOMER" + [mainAspectType]="MainAspectType.AS_PLANNED" > diff --git a/frontend/src/app/modules/page/other-parts/presentation/supplier-parts/supplier-parts.component.html b/frontend/src/app/modules/page/other-parts/presentation/supplier-parts/supplier-parts.component.html index 82e8ccceed..a480d772f2 100644 --- a/frontend/src/app/modules/page/other-parts/presentation/supplier-parts/supplier-parts.component.html +++ b/frontend/src/app/modules/page/other-parts/presentation/supplier-parts/supplier-parts.component.html @@ -46,6 +46,7 @@ [multiSortList]="tableSupplierAsBuiltSortList" (filterActivated)="filterActivated(true, $event )" [tableType]="PartTableType.AS_BUILT_SUPPLIER" + [mainAspectType]="MainAspectType.AS_BUILT" > @@ -77,6 +78,7 @@ [multiSelectActive]="true" (filterActivated)="filterActivated(false, $event )" [tableType]="PartTableType.AS_PLANNED_SUPPLIER" + [mainAspectType]="MainAspectType.AS_PLANNED" > diff --git a/frontend/src/app/modules/shared/components/card-metric/card-metric.component.html b/frontend/src/app/modules/shared/components/card-metric/card-metric.component.html index a0e15757a0..94f1c93135 100644 --- a/frontend/src/app/modules/shared/components/card-metric/card-metric.component.html +++ b/frontend/src/app/modules/shared/components/card-metric/card-metric.component.html @@ -5,7 +5,9 @@

{{((value | async)?.data | number) | abbreviateNumber}}

+ From 1ec4d53f9a200a86421f02ad4686c0a877c4b869 Mon Sep 17 00:00:00 2001 From: Martin Maul Date: Thu, 23 Nov 2023 14:15:35 +0100 Subject: [PATCH 13/17] feature(UI):[TRACEFOSS-604] remove unit --- .../dashboard/abstraction/dashboard.facade.ts | 19 +++++++++---------- .../card-metric/card-metric.component.html | 2 +- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/frontend/src/app/modules/page/dashboard/abstraction/dashboard.facade.ts b/frontend/src/app/modules/page/dashboard/abstraction/dashboard.facade.ts index 765bbf817a..6b9c2b9ecd 100644 --- a/frontend/src/app/modules/page/dashboard/abstraction/dashboard.facade.ts +++ b/frontend/src/app/modules/page/dashboard/abstraction/dashboard.facade.ts @@ -19,15 +19,15 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ -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 {Observable, Subscription} from 'rxjs'; -import {DashboardService} from '../core/dashboard.service'; -import {DashboardState} from '../core/dashboard.state'; -import {DashboardStats} from '../model/dashboard.model'; +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 { 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 { @@ -41,7 +41,6 @@ export class DashboardFacade { constructor( private readonly dashboardService: DashboardService, private readonly dashboardState: DashboardState, - // private readonly partsService: PartsService, private readonly investigationsService: InvestigationsService, private readonly alertsService: AlertsService ) {} diff --git a/frontend/src/app/modules/shared/components/card-metric/card-metric.component.html b/frontend/src/app/modules/shared/components/card-metric/card-metric.component.html index 94f1c93135..e875822190 100644 --- a/frontend/src/app/modules/shared/components/card-metric/card-metric.component.html +++ b/frontend/src/app/modules/shared/components/card-metric/card-metric.component.html @@ -5,7 +5,7 @@

{{((value | async)?.data | number) | abbreviateNumber}}

- diff --git a/frontend/src/app/modules/shared/pipes/abbreviate-number.pipe.ts b/frontend/src/app/modules/shared/pipes/abbreviate-number.pipe.ts index 8668af6422..35ac814ead 100644 --- a/frontend/src/app/modules/shared/pipes/abbreviate-number.pipe.ts +++ b/frontend/src/app/modules/shared/pipes/abbreviate-number.pipe.ts @@ -2,7 +2,6 @@ import { Pipe, PipeTransform } from '@angular/core'; @Pipe({ name: 'abbreviateNumber', pure: false }) export class AbbreviateNumberPipe implements PipeTransform { - constructor() {} transform(value: string ): string { if(!value) {