diff --git a/DEPENDENCIES_BACKEND b/DEPENDENCIES_BACKEND index 754930490a..6b5ca2b1cf 100644 --- a/DEPENDENCIES_BACKEND +++ b/DEPENDENCIES_BACKEND @@ -333,9 +333,9 @@ maven/mavencentral/org.jboss.logging/jboss-logging/3.5.3.Final, Apache-2.0, appr maven/mavencentral/org.jetbrains.kotlin/kotlin-stdlib-common/1.9.0, Apache-2.0, approved, #14186 maven/mavencentral/org.jetbrains.kotlin/kotlin-stdlib-common/1.9.23, Apache-2.0, approved, #14186 maven/mavencentral/org.jetbrains.kotlin/kotlin-stdlib-jdk7/1.6.10, Apache-2.0, approved, clearlydefined -maven/mavencentral/org.jetbrains.kotlin/kotlin-stdlib-jdk7/1.9.23, None, restricted, #14188 +maven/mavencentral/org.jetbrains.kotlin/kotlin-stdlib-jdk7/1.9.23, Apache-2.0, approved, #14188 maven/mavencentral/org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.6.10, Apache-2.0, approved, clearlydefined -maven/mavencentral/org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.9.23, None, restricted, #14185 +maven/mavencentral/org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.9.23, Apache-2.0, approved, #14185 maven/mavencentral/org.jetbrains.kotlin/kotlin-stdlib/1.6.20, Apache-2.0, approved, clearlydefined maven/mavencentral/org.jetbrains.kotlin/kotlin-stdlib/1.9.23, Apache-2.0, approved, #11827 maven/mavencentral/org.jetbrains/annotations/24.1.0, Apache-2.0, approved, clearlydefined diff --git a/docs/src/uml-diagrams/arc42/runtime-view/policies/policy-assets.puml b/docs/src/uml-diagrams/arc42/runtime-view/policies/policy-assets.puml index ab6f6b49d2..156295f813 100644 --- a/docs/src/uml-diagrams/arc42/runtime-view/policies/policy-assets.puml +++ b/docs/src/uml-diagrams/arc42/runtime-view/policies/policy-assets.puml @@ -5,18 +5,11 @@ skinparam defaultFontName "Architects daughter" title Sequence diagram: Policy handling on asset provisioning flow participant "Trace-X" as TraceX -participant "EDC Consumer" as EdcConsumer participant "EDC Provider (other)" as EdcProvider activate TraceX TraceX -> TraceX: Publish asset to core services -TraceX -> EdcConsumer: Register policy -activate EdcConsumer -EdcConsumer -> EdcProvider: Register policy -activate EdcProvider -EdcProvider --> EdcConsumer: Ok -deactivate EdcProvider -EdcConsumer --> TraceX: Ok -deactivate EdcConsumer +TraceX -> EdcProvider: Register policy +EdcProvider --> TraceX: Ok TraceX -> TraceX: Reuse policy for contract definition creation @enduml 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 db6fe92728..b2fecb0b4d 100644 --- a/frontend/src/app/modules/page/dashboard/abstraction/dashboard.facade.ts +++ b/frontend/src/app/modules/page/dashboard/abstraction/dashboard.facade.ts @@ -20,6 +20,7 @@ ********************************************************************************/ import { Injectable } from '@angular/core'; +import { NotificationChannel } from '@shared/components/multi-select-autocomplete/table-type.model'; import { Notifications } from '@shared/model/notification.model'; import { View } from '@shared/model/view.model'; import { NotificationService } from '@shared/service/notification.service'; @@ -97,7 +98,7 @@ export class DashboardFacade { private setReceivedInvestigations(): void { this.investigationsReceivedSubscription?.unsubscribe(); - this.investigationsReceivedSubscription = this.notificationService.getReceived(0, 5, [ [ 'createdDate', 'desc' ] ], null, null, true).subscribe({ + this.investigationsReceivedSubscription = this.notificationService.getNotifications(0, 5, [ [ 'createdDate', 'desc' ] ], NotificationChannel.RECEIVER, null, null).subscribe({ next: data => this.dashboardState.setRecentReceivedInvestigations({ data }), error: (error: Error) => this.dashboardState.setRecentReceivedInvestigations({ error }), }); @@ -105,7 +106,7 @@ export class DashboardFacade { private setCreatedInvestigations(): void { this.investigationsCreatedSubscription?.unsubscribe(); - this.investigationsCreatedSubscription = this.notificationService.getCreated(0, 5, [ [ 'createdDate', 'desc' ] ], null, null, true).subscribe({ + this.investigationsCreatedSubscription = this.notificationService.getNotifications(0, 5, [ [ 'createdDate', 'desc' ] ], NotificationChannel.SENDER, null, null).subscribe({ next: data => this.dashboardState.setRecentCreatedInvestigations({ data }), error: (error: Error) => this.dashboardState.setRecentCreatedInvestigations({ error }), }); @@ -113,7 +114,7 @@ export class DashboardFacade { private setReceivedAlerts(): void { this.alertsReceivedSubscription?.unsubscribe(); - this.alertsReceivedSubscription = this.notificationService.getReceived(0, 5, [ [ 'createdDate', 'desc' ] ], null, null, false).subscribe({ + this.alertsReceivedSubscription = this.notificationService.getNotifications(0, 5, [ [ 'createdDate', 'desc' ] ], NotificationChannel.RECEIVER, null, null).subscribe({ next: data => this.dashboardState.setRecentReceivedAlerts({ data }), error: (error: Error) => this.dashboardState.setRecentReceivedAlerts({ error }), }); @@ -122,7 +123,7 @@ export class DashboardFacade { private setCreatedAlerts(): void { this.alertsCreatedSubscription?.unsubscribe(); - this.alertsCreatedSubscription = this.notificationService.getCreated(0, 5, [ [ 'createdDate', 'desc' ] ], null, null, false).subscribe({ + this.alertsCreatedSubscription = this.notificationService.getNotifications(0, 5, [ [ 'createdDate', 'desc' ] ], NotificationChannel.SENDER, null, null).subscribe({ next: data => this.dashboardState.setRecentCreatedAlerts({ data }), error: (error: Error) => this.dashboardState.setRecentCreatedAlerts({ error }), }); 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 b703b7cacf..522bc44916 100644 --- a/frontend/src/app/modules/page/dashboard/presentation/dashboard.component.html +++ b/frontend/src/app/modules/page/dashboard/presentation/dashboard.component.html @@ -114,10 +114,9 @@

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 e3a55fe9d9..e34539dab0 100644 --- a/frontend/src/app/modules/page/dashboard/presentation/dashboard.component.ts +++ b/frontend/src/app/modules/page/dashboard/presentation/dashboard.component.ts @@ -21,16 +21,11 @@ import { Component, OnDestroy, OnInit } from '@angular/core'; import { Router } from '@angular/router'; -import { NOTIFICATION_BASE_ROUTE, getRoute } from '@core/known-route'; +import { getRoute, NOTIFICATION_BASE_ROUTE } from '@core/known-route'; import { DashboardStats } from '@page/dashboard/model/dashboard.model'; import { MetricData } from '@page/dashboard/presentation/dashboard.model'; import { TableType } from '@shared/components/multi-select-autocomplete/table-type.model'; -import { - Notification, - Notifications, - NotificationStatusGroup, - NotificationType, -} from '@shared/model/notification.model'; +import { Notification, Notifications, NotificationStatusGroup } from '@shared/model/notification.model'; import { View } from '@shared/model/view.model'; import { Observable } from 'rxjs'; import { map } from 'rxjs/operators'; @@ -134,6 +129,5 @@ export class DashboardComponent implements OnInit, OnDestroy { this.router.navigate([ `/${ link }/${ notification.id }` ]).then(); } - protected readonly NotificationType = NotificationType; protected readonly TableType = TableType; } diff --git a/frontend/src/app/modules/page/notifications/core/notification-helper.service.ts b/frontend/src/app/modules/page/notifications/core/notification-helper.service.ts index 2407130f1c..f687662415 100644 --- a/frontend/src/app/modules/page/notifications/core/notification-helper.service.ts +++ b/frontend/src/app/modules/page/notifications/core/notification-helper.service.ts @@ -19,6 +19,7 @@ import { Injectable } from '@angular/core'; import { NotificationsFacade } from '@page/notifications/core/notifications.facade'; +import { NotificationStatus } from '@shared/model/notification.model'; import { Observable } from 'rxjs'; @Injectable({ @@ -52,4 +53,24 @@ export class NotificationHelperService { return this.notificationsFacade.declineNotification(id, reason); } + modalCallback(status: NotificationStatus, id: string, reason?: string): Observable { + switch (status) { + case NotificationStatus.ACKNOWLEDGED: + return this.notificationsFacade.acknowledgeNotification(id); + case NotificationStatus.APPROVED: + return this.notificationsFacade.approveNotification(id); + case NotificationStatus.CANCELED: + return this.notificationsFacade.cancelNotification(id); + case NotificationStatus.CLOSED: + return this.notificationsFacade.closeNotification(id, reason); + case NotificationStatus.ACCEPTED: + return this.notificationsFacade.acceptNotification(id, reason); + case NotificationStatus.DECLINED: + return this.notificationsFacade.declineNotification(id, reason); + default: + return null; + } + } + + } diff --git a/frontend/src/app/modules/page/notifications/core/notifications.facade.ts b/frontend/src/app/modules/page/notifications/core/notifications.facade.ts index b59b9d0494..79734f395b 100644 --- a/frontend/src/app/modules/page/notifications/core/notifications.facade.ts +++ b/frontend/src/app/modules/page/notifications/core/notifications.facade.ts @@ -20,6 +20,7 @@ import { Injectable } from '@angular/core'; import { NotificationsState } from '@page/notifications/core/notifications.state'; import { provideDataObject } from '@page/parts/core/parts.helper'; +import { NotificationChannel } from '@shared/components/multi-select-autocomplete/table-type.model'; import { TableHeaderSort } from '@shared/components/table/table.model'; import { Notification, @@ -51,13 +52,13 @@ export class NotificationsFacade { } public getNotification(id: string): Observable { - return this.notificationService.getNotificationById(id, false); + return this.notificationService.getNotificationById(id); } public setReceivedNotifications(page = 0, pageSize = 50, sorting: TableHeaderSort[] = [], filter?: NotificationDeeplinkFilter, fullFilter?: any): void { this.notificationReceivedSubscription?.unsubscribe(); this.notificationReceivedSubscription = this.notificationService - .getReceived(page, pageSize, sorting, filter, fullFilter, false) + .getNotifications(page, pageSize, sorting, NotificationChannel.RECEIVER, filter, fullFilter) .subscribe({ next: data => (this.notificationsState.notificationsReceived = { data: provideDataObject(data) }), error: (error: Error) => (this.notificationsState.notificationsReceived = { error }), @@ -67,7 +68,7 @@ export class NotificationsFacade { public setQueuedAndRequestedNotifications(page = 0, pageSize = 50, sorting: TableHeaderSort[] = [], filter?: NotificationDeeplinkFilter, fullFilter?: any): void { this.notificationQueuedAndRequestedSubscription?.unsubscribe(); this.notificationQueuedAndRequestedSubscription = this.notificationService - .getCreated(page, pageSize, sorting, filter, fullFilter, false) + .getNotifications(page, pageSize, sorting, NotificationChannel.SENDER, filter, fullFilter) .subscribe({ next: data => (this.notificationsState.notificationsQueuedAndRequested = { data: provideDataObject(data) }), error: (error: Error) => (this.notificationsState.notificationsQueuedAndRequested = { error }), @@ -81,26 +82,26 @@ export class NotificationsFacade { public closeNotification(notificationId: string, reason: string): Observable { - return this.notificationService.closeNotification(notificationId, reason, false); + return this.notificationService.closeNotification(notificationId, reason); } public approveNotification(notificationId: string): Observable { - return this.notificationService.approveNotification(notificationId, false); + return this.notificationService.approveNotification(notificationId); } public cancelNotification(notificationId: string): Observable { - return this.notificationService.cancelNotification(notificationId, false); + return this.notificationService.cancelNotification(notificationId); } public acknowledgeNotification(notificationId: string): Observable { - return this.notificationService.updateNotification(notificationId, NotificationStatus.ACKNOWLEDGED, null, false); + return this.notificationService.updateNotification(notificationId, NotificationStatus.ACKNOWLEDGED, null); } public acceptNotification(notificationId: string, reason: string): Observable { - return this.notificationService.updateNotification(notificationId, NotificationStatus.ACCEPTED, reason, false); + return this.notificationService.updateNotification(notificationId, NotificationStatus.ACCEPTED, reason); } public declineNotification(notificationId: string, reason: string): Observable { - return this.notificationService.updateNotification(notificationId, NotificationStatus.DECLINED, reason, false); + return this.notificationService.updateNotification(notificationId, NotificationStatus.DECLINED, reason); } } diff --git a/frontend/src/app/modules/page/notifications/detail/notification-detail.component.html b/frontend/src/app/modules/page/notifications/detail/notification-detail.component.html index 4ddcb98805..d86fcc533b 100644 --- a/frontend/src/app/modules/page/notifications/detail/notification-detail.component.html +++ b/frontend/src/app/modules/page/notifications/detail/notification-detail.component.html @@ -40,7 +40,7 @@ > @@ -60,7 +60,7 @@ > @@ -80,7 +80,7 @@ > @@ -100,7 +100,7 @@ > @@ -120,7 +120,7 @@ > @@ -140,7 +140,7 @@ > @@ -157,7 +157,6 @@ (confirmActionCompleted)="handleConfirmActionCompletedEvent()" [helperService]="helperService" [selectedNotification]="selectedNotification" - [translationContext]="TranslationContext.COMMONALERT" >
diff --git a/frontend/src/app/modules/page/notifications/detail/notification-detail.component.ts b/frontend/src/app/modules/page/notifications/detail/notification-detail.component.ts index 1b9757e9e0..ff80d00e3f 100644 --- a/frontend/src/app/modules/page/notifications/detail/notification-detail.component.ts +++ b/frontend/src/app/modules/page/notifications/detail/notification-detail.component.ts @@ -28,7 +28,7 @@ import { NotificationActionHelperService } from '@shared/assembler/notification- import { NotificationCommonModalComponent } from '@shared/components/notification-common-modal/notification-common-modal.component'; import { CreateHeaderFromColumns, TableConfig, TableEventConfig } from '@shared/components/table/table.model'; import { ToastService } from '@shared/components/toasts/toast.service'; -import { Notification, NotificationType } from '@shared/model/notification.model'; +import { Notification, NotificationStatus, NotificationType } from '@shared/model/notification.model'; import { TranslationContext } from '@shared/model/translation-context.model'; import { View } from '@shared/model/view.model'; import { NotificationAction } from '@shared/modules/notification/notification-action.enum'; @@ -196,7 +196,7 @@ export class NotificationDetailComponent implements AfterViewInit, OnDestroy { } private selectedNotificationBasedOnUrl(): void { - const notificationId = this.route.snapshot.paramMap.get('alertId'); + const notificationId = this.route.snapshot.paramMap.get('notificationId'); this.notificationsFacade .getNotification(notificationId) .pipe( @@ -206,8 +206,8 @@ export class NotificationDetailComponent implements AfterViewInit, OnDestroy { .subscribe(); } - protected readonly TranslationContext = TranslationContext; protected readonly NotificationType = NotificationType; protected readonly NotificationAction = NotificationAction; + protected readonly NotificationStatus = NotificationStatus; } diff --git a/frontend/src/app/modules/page/notifications/presentation/notifications.component.html b/frontend/src/app/modules/page/notifications/presentation/notifications.component.html index 3eae3d38bb..19f3909881 100644 --- a/frontend/src/app/modules/page/notifications/presentation/notifications.component.html +++ b/frontend/src/app/modules/page/notifications/presentation/notifications.component.html @@ -20,14 +20,11 @@ diff --git a/frontend/src/app/modules/page/notifications/presentation/notifications.component.ts b/frontend/src/app/modules/page/notifications/presentation/notifications.component.ts index afeef13beb..c9d112c859 100644 --- a/frontend/src/app/modules/page/notifications/presentation/notifications.component.ts +++ b/frontend/src/app/modules/page/notifications/presentation/notifications.component.ts @@ -52,7 +52,6 @@ export class NotificationsComponent { public readonly notificationsReceived$; public readonly notificationsQueuedAndRequested$; - public isInvestigation = false; public menuActionsConfig: MenuActionConfig[]; public notificationReceivedSortList: TableHeaderSort[] = []; diff --git a/frontend/src/app/modules/shared/assembler/notificationMenuActions.assembler.spec.ts b/frontend/src/app/modules/shared/assembler/notificationMenuActions.assembler.spec.ts index 24a9821b33..9bcf57678a 100644 --- a/frontend/src/app/modules/shared/assembler/notificationMenuActions.assembler.spec.ts +++ b/frontend/src/app/modules/shared/assembler/notificationMenuActions.assembler.spec.ts @@ -28,7 +28,6 @@ import { NotificationMenuActionsAssembler } from '@shared/assembler/notification import { NotificationCommonModalComponent } from '@shared/components/notification-common-modal/notification-common-modal.component'; import { Notification, NotificationStatus } from '@shared/model/notification.model'; import { Severity } from '@shared/model/severity.model'; -import { CloseNotificationModalComponent } from '@shared/modules/notification/modal/close/close-notification-modal.component'; import { KeycloakService } from 'keycloak-angular'; describe('NotificationMenuActionsAssembler', () => { @@ -52,7 +51,6 @@ describe('NotificationMenuActionsAssembler', () => { NotificationActionHelperService, NotificationCommonModalComponent, NotificationMenuActionsAssembler, - CloseNotificationModalComponent, ], }); notificationCommonModalComponent = TestBed.inject(NotificationCommonModalComponent); diff --git a/frontend/src/app/modules/shared/assembler/notificationMenuActions.assembler.ts b/frontend/src/app/modules/shared/assembler/notificationMenuActions.assembler.ts index 2217e506b6..aec5385a9e 100644 --- a/frontend/src/app/modules/shared/assembler/notificationMenuActions.assembler.ts +++ b/frontend/src/app/modules/shared/assembler/notificationMenuActions.assembler.ts @@ -19,7 +19,7 @@ import { NotificationActionHelperService } from '@shared/assembler/notification-action-helper.service'; import { NotificationCommonModalComponent } from '@shared/components/notification-common-modal/notification-common-modal.component'; import { MenuActionConfig } from '@shared/components/table/table.model'; -import { Notification } from '@shared/model/notification.model'; +import { Notification, NotificationStatus } from '@shared/model/notification.model'; import { NotificationAction } from '@shared/modules/notification/notification-action.enum'; export class NotificationMenuActionsAssembler { @@ -28,42 +28,42 @@ export class NotificationMenuActionsAssembler { { label: 'actions.close', icon: 'close', - action: data => modal.show('close', data), + action: data => modal.show(NotificationStatus.CLOSED, data), condition: data => helperService.showCloseButton(data), isAuthorized: helperService.isAuthorizedForButton(NotificationAction.CLOSE), }, { label: 'actions.approve', icon: 'share', - action: data => modal.show('approve', data), + action: data => modal.show(NotificationStatus.APPROVED, data), condition: data => helperService.showApproveButton(data), isAuthorized: helperService.isAuthorizedForButton(NotificationAction.APPROVE), }, { label: 'actions.cancel', icon: 'cancel', - action: data => modal.show('cancel', data), + action: data => modal.show(NotificationStatus.CANCELED, data), condition: data => helperService.showCancelButton(data), isAuthorized: helperService.isAuthorizedForButton(NotificationAction.CANCEL), }, { label: 'actions.acknowledge', icon: 'work', - action: data => modal.show('acknowledge', data), + action: data => modal.show(NotificationStatus.ACKNOWLEDGED, data), condition: data => helperService.showAcknowledgeButton(data), isAuthorized: helperService.isAuthorizedForButton(NotificationAction.ACKNOWLEDGE), }, { label: 'actions.accept', icon: 'assignment_turned_in', - action: data => modal.show('accept', data), + action: data => modal.show(NotificationStatus.ACCEPTED, data), condition: data => helperService.showAcceptButton(data), isAuthorized: helperService.isAuthorizedForButton(NotificationAction.ACCEPT), }, { label: 'actions.decline', icon: 'assignment_late', - action: data => modal.show('decline', data), + action: data => modal.show(NotificationStatus.DECLINED, data), condition: data => helperService.showDeclineButton(data), isAuthorized: helperService.isAuthorizedForButton(NotificationAction.DECLINE), }, diff --git a/frontend/src/app/modules/shared/components/multi-select-autocomplete/autocomplete-strategy.spec.ts b/frontend/src/app/modules/shared/components/multi-select-autocomplete/autocomplete-strategy.spec.ts index efc284ae60..4607c7c09d 100644 --- a/frontend/src/app/modules/shared/components/multi-select-autocomplete/autocomplete-strategy.spec.ts +++ b/frontend/src/app/modules/shared/components/multi-select-autocomplete/autocomplete-strategy.spec.ts @@ -16,13 +16,13 @@ * * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ -import {Owner} from '@page/parts/model/owner.enum'; +import { Owner } from '@page/parts/model/owner.enum'; import { channelOfNotification, getOwnerOfTable, isAsBuilt, } from '@shared/components/multi-select-autocomplete/autocomplete-strategy'; -import {NotificationChannel, TableType} from '@shared/components/multi-select-autocomplete/table-type.model'; +import { NotificationChannel, TableType } from '@shared/components/multi-select-autocomplete/table-type.model'; describe('Autocomplete Strategies', () => { @@ -46,9 +46,7 @@ describe('Autocomplete Strategies', () => { }); it('should determine channel of notification', async () => { - expect(channelOfNotification(TableType.CREATED_INVESTIGATION)).toBe(NotificationChannel.SENDER); - expect(channelOfNotification(TableType.CREATED_ALERT)).toBe(NotificationChannel.SENDER); - expect(channelOfNotification(TableType.RECEIVED_INVESTIGATION)).toBe(NotificationChannel.RECEIVER); - expect(channelOfNotification(TableType.RECEIVED_INVESTIGATION)).toBe(NotificationChannel.RECEIVER); + expect(channelOfNotification(TableType.SENT_NOTIFICATION)).toBe(NotificationChannel.SENDER); + expect(channelOfNotification(TableType.RECEIVED_NOTIFICATION)).toBe(NotificationChannel.RECEIVER); }); }); diff --git a/frontend/src/app/modules/shared/components/multi-select-autocomplete/autocomplete-strategy.ts b/frontend/src/app/modules/shared/components/multi-select-autocomplete/autocomplete-strategy.ts index 2a3fc4da18..844312b540 100644 --- a/frontend/src/app/modules/shared/components/multi-select-autocomplete/autocomplete-strategy.ts +++ b/frontend/src/app/modules/shared/components/multi-select-autocomplete/autocomplete-strategy.ts @@ -53,7 +53,7 @@ export class PartsStrategy extends AutocompleteStrategy { @Injectable({ providedIn: 'any', }) -export class InvestigationStrategy extends AutocompleteStrategy { +export class NotificationStrategy extends AutocompleteStrategy { notificationService: NotificationService; constructor(notificationService: NotificationService) { @@ -66,30 +66,7 @@ export class InvestigationStrategy extends AutocompleteStrategy { return this.notificationService.getDistinctFilterValues( notificationChannel, filterColumns, - searchElement, - true, - ); - } -} - -@Injectable({ - providedIn: 'any', -}) -export class AlertStrategy extends AutocompleteStrategy { - notificationService: NotificationService; - - constructor(notificationService: NotificationService) { - super(); - this.notificationService = notificationService; - } - - retrieveSuggestionValues(tableType: TableType, filterColumns: string, searchElement: string): any { - const notificationChannel = channelOfNotification(tableType); - return this.notificationService.getDistinctFilterValues( - notificationChannel, - filterColumns, - searchElement, - false, + searchElement ); } } @@ -120,11 +97,9 @@ export const AutocompleteStrategyMap = new Map([ [ TableType.AS_PLANNED_OWN, PartsStrategy ], [ TableType.AS_PLANNED_CUSTOMER, PartsStrategy ], [ TableType.AS_PLANNED_SUPPLIER, PartsStrategy ], - [ TableType.RECEIVED_INVESTIGATION, InvestigationStrategy ], - [ TableType.CREATED_INVESTIGATION, InvestigationStrategy ], - [ TableType.RECEIVED_ALERT, AlertStrategy ], - [ TableType.CREATED_ALERT, AlertStrategy ], - [ TableType.CONTRACTS, ContractsStrategy] + [ TableType.RECEIVED_NOTIFICATION, NotificationStrategy ], + [ TableType.SENT_NOTIFICATION, NotificationStrategy ], + [ TableType.CONTRACTS, ContractsStrategy ], ]); export function getOwnerOfTable(tableType: TableType): Owner { @@ -145,14 +120,10 @@ export function isAsBuilt(tableType: TableType): boolean { } export function channelOfNotification(tableType: TableType): NotificationChannel { - if (tableType === TableType.CREATED_ALERT || tableType === TableType.CREATED_INVESTIGATION) { + if (tableType === TableType.SENT_NOTIFICATION) { return NotificationChannel.SENDER; } else { return NotificationChannel.RECEIVER; } } - -export function isInvestigation(tableType: TableType): boolean { - return [ TableType.RECEIVED_INVESTIGATION, TableType.CREATED_INVESTIGATION ].includes(tableType); -} diff --git a/frontend/src/app/modules/shared/components/multi-select-autocomplete/table-type.model.ts b/frontend/src/app/modules/shared/components/multi-select-autocomplete/table-type.model.ts index a87d2e271c..b18f55f3a0 100644 --- a/frontend/src/app/modules/shared/components/multi-select-autocomplete/table-type.model.ts +++ b/frontend/src/app/modules/shared/components/multi-select-autocomplete/table-type.model.ts @@ -24,10 +24,8 @@ export enum TableType { AS_BUILT_CUSTOMER = 'AS_BUILT_CUSTOMER', AS_PLANNED_SUPPLIER = 'AS_PLANNED_SUPPLIER', AS_PLANNED_CUSTOMER = 'AS_PLANNED_CUSTOMER', - RECEIVED_INVESTIGATION = 'RECEIVED_INVESTIGATION', - CREATED_INVESTIGATION = 'CREATED_INVESTIGATION', - RECEIVED_ALERT = 'RECEIVED_ALERT', - CREATED_ALERT = 'CREATED_ALERT', + RECEIVED_NOTIFICATION = 'RECEIVED_NOTIFICATION', + SENT_NOTIFICATION = 'SENT_NOTIFICATION', CONTRACTS='CONTRACTS' } diff --git a/frontend/src/app/modules/shared/components/notification-common-modal/notification-common-modal.component.html b/frontend/src/app/modules/shared/components/notification-common-modal/notification-common-modal.component.html index 8659bd5efe..82aac18b29 100644 --- a/frontend/src/app/modules/shared/components/notification-common-modal/notification-common-modal.component.html +++ b/frontend/src/app/modules/shared/components/notification-common-modal/notification-common-modal.component.html @@ -16,36 +16,8 @@ SPDX-License-Identifier: Apache-2.0 --> - + - - - - - - - - + [callback]="helperService.modalCallback.bind(this)" +> diff --git a/frontend/src/app/modules/shared/components/notification-common-modal/notification-common-modal.component.ts b/frontend/src/app/modules/shared/components/notification-common-modal/notification-common-modal.component.ts index cbbef22108..30cceeeeb0 100644 --- a/frontend/src/app/modules/shared/components/notification-common-modal/notification-common-modal.component.ts +++ b/frontend/src/app/modules/shared/components/notification-common-modal/notification-common-modal.component.ts @@ -19,14 +19,8 @@ import { Component, EventEmitter, Input, Optional, Output, ViewChild } from '@angular/core'; import { NotificationHelperService } from '@page/notifications/core/notification-helper.service'; import { NotificationsFacade } from '@page/notifications/core/notifications.facade'; -import { Notification } from '@shared/model/notification.model'; -import { TranslationContext } from '@shared/model/translation-context.model'; -import { AcceptNotificationModalComponent } from '@shared/modules/notification/modal/accept/accept-notification-modal.component'; -import { AcknowledgeNotificationModalComponent } from '@shared/modules/notification/modal/acknowledge/acknowledge-notification-modal.component'; -import { ApproveNotificationModalComponent } from '@shared/modules/notification/modal/approve/approve-notification-modal.component'; -import { CancelNotificationModalComponent } from '@shared/modules/notification/modal/cancel/cancel-notification-modal.component'; -import { CloseNotificationModalComponent } from '@shared/modules/notification/modal/close/close-notification-modal.component'; -import { DeclineNotificationModalComponent } from '@shared/modules/notification/modal/decline/decline-notification-modal.component'; +import { Notification, NotificationStatus } from '@shared/model/notification.model'; +import { NotificationActionModalComponent } from '@shared/modules/notification/modal/actions/notification-action-modal.component'; @Component({ selector: 'app-notification-common-modal', @@ -35,18 +29,10 @@ import { DeclineNotificationModalComponent } from '@shared/modules/notification/ export class NotificationCommonModalComponent { @Input() selectedNotification: Notification; - @Input() translationContext: TranslationContext; @Input() helperService: NotificationHelperService; @Output() confirmActionCompleted = new EventEmitter(); - - @ViewChild(ApproveNotificationModalComponent) approveModal: ApproveNotificationModalComponent; - @ViewChild(CloseNotificationModalComponent) closeModal: CloseNotificationModalComponent; - @ViewChild(CancelNotificationModalComponent) cancelModal: CancelNotificationModalComponent; - - @ViewChild(AcceptNotificationModalComponent) acceptModal: AcceptNotificationModalComponent; - @ViewChild(AcknowledgeNotificationModalComponent) acknowledgeModal: AcknowledgeNotificationModalComponent; - @ViewChild(DeclineNotificationModalComponent) declineModal: DeclineNotificationModalComponent; + @ViewChild(NotificationActionModalComponent) notificationActionModalComponent: NotificationActionModalComponent; // TODO do not delete the facade here. This will lead to a nullpointer exception within the modal call. public constructor( @@ -59,36 +45,12 @@ export class NotificationCommonModalComponent { this.confirmActionCompleted.emit(); } - public show(modalContext: string, notification?: Notification) { + public show(desiredStatus: NotificationStatus, notification?: Notification) { let notificationToShow = notification || this.selectedNotification; - switch (modalContext) { - case 'approve': { - this.approveModal.show(notificationToShow); - break; - } - case 'close': { - this.closeModal.show(notificationToShow); - break; - } - case 'cancel': { - this.cancelModal.show(notificationToShow); - break; - } - case 'accept': { - this.acceptModal.show(notificationToShow); - break; - } - case 'acknowledge': { - this.acknowledgeModal.show(notificationToShow); - break; - } - case 'decline': { - this.declineModal.show(notificationToShow); - break; - } - } + console.log(notificationToShow, "notification"); + console.log(desiredStatus, "desiredstatus"); + this.notificationActionModalComponent.show(notificationToShow, desiredStatus); } - - protected readonly TranslationContext = TranslationContext; + protected readonly NotificationStatus = NotificationStatus; } diff --git a/frontend/src/app/modules/shared/components/notification-reason/notification-reason.component.spec.ts b/frontend/src/app/modules/shared/components/notification-reason/notification-reason.component.spec.ts deleted file mode 100644 index 2967b2f40c..0000000000 --- a/frontend/src/app/modules/shared/components/notification-reason/notification-reason.component.spec.ts +++ /dev/null @@ -1,58 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2022, 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * Copyright (c) 2022, 2023 ZF Friedrichshafen AG - * Copyright (c) 2022, 2023 Contributors to the Eclipse Foundation - * - * See the NOTICE file(s) distributed with this work for additional - * information regarding copyright ownership. - * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0. - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - * - * SPDX-License-Identifier: Apache-2.0 - ********************************************************************************/ - -import { NotificationStatus } from '@shared/model/notification.model'; -import { notificationTemplate } from '@shared/modules/notification/modal/modalTestHelper.spec'; -import { SharedModule } from '@shared/shared.module'; -import { screen } from '@testing-library/angular'; -import { renderComponent } from '@tests/test-render.utils'; - -describe('NotificationReasonComponent', () => { - const defaultNotification = Object.assign({ ...notificationTemplate }); - const renderReason = (notification: Notification = defaultNotification) => { - return renderComponent(``, { - imports: [ SharedModule ], - componentProperties: { notification }, - }); - }; - - it('should render description', async () => { - await renderReason(); - expect(screen.getByText(defaultNotification.description)).toBeInTheDocument(); - }); - - it('should render accept reason with sent status', async () => { - const reason = { accept: 'Accept reason', close: '', decline: '' }; - const status = NotificationStatus.SENT; - - await renderReason({ ...defaultNotification, reason, status }); - expect(screen.getByText(reason.accept)).toBeInTheDocument(); - expect(screen.getByText('commonAlert.status.SENT')).toBeInTheDocument(); - expect(screen.getByText(defaultNotification.createdByName)).toBeInTheDocument(); - expect(screen.getByText(defaultNotification.sendToName)).toBeInTheDocument(); - }); - - - it('should render username from sender', async () => { - await renderReason(); - expect(screen.getByText(defaultNotification.createdByName)).toBeInTheDocument(); - }); -}); diff --git a/frontend/src/app/modules/shared/components/request-notification/request-notification.component.ts b/frontend/src/app/modules/shared/components/request-notification/request-notification.component.ts index 5e8c59463b..8f3f0bceaf 100644 --- a/frontend/src/app/modules/shared/components/request-notification/request-notification.component.ts +++ b/frontend/src/app/modules/shared/components/request-notification/request-notification.component.ts @@ -95,12 +95,15 @@ export class RequestNotificationComponent { // set asBuilt parameter if one of the selectedItems are a asPlanned Part const isAsBuilt = this.selectedItems.map(part => part.semanticDataModel === SemanticDataModel.PARTASPLANNED).includes(true); - const { description, bpn, severity } = this.formGroup.value; + let { description, bpn, severity, title } = this.formGroup.value; const { link, queryParams } = getRoute(NOTIFICATION_BASE_ROUTE, NotificationStatusGroup.QUEUED_AND_REQUESTED); let type = this.isInvestigation ? 'INVESTIGATION' : 'ALERT'; + if (title === ""){ + title = null; + } - this.notificationService.createNotification(partIds, description, severity, bpn, isAsBuilt, type).subscribe({ + this.notificationService.createNotification(partIds, description, severity, bpn, isAsBuilt, type, title).subscribe({ next: () => this.onSuccessfulSubmit(link, queryParams), error: (err) => this.onUnsuccessfulSubmit(err.error.message), }); diff --git a/frontend/src/app/modules/shared/helper/notification-helper.ts b/frontend/src/app/modules/shared/helper/notification-helper.ts index 85a1385ce5..359744cb5f 100644 --- a/frontend/src/app/modules/shared/helper/notification-helper.ts +++ b/frontend/src/app/modules/shared/helper/notification-helper.ts @@ -18,7 +18,8 @@ * * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ -import { NotificationDeeplinkFilter } from '@shared/model/notification.model'; +import {Notification, NotificationDeeplinkFilter, NotificationType} from '@shared/model/notification.model'; +import {TranslationContext} from "@shared/model/translation-context.model"; export interface DeeplinkNotificationFilter { receivedFilter: NotificationDeeplinkFilter, @@ -38,3 +39,11 @@ export function createDeeplinkNotificationFilter(params: any): DeeplinkNotificat return { receivedFilter, sentFilter }; } } + +export function getTranslationContext(notification: Notification):TranslationContext{ + if (notification?.type === NotificationType.ALERT.valueOf()){ + return TranslationContext.COMMONALERT + } else { + return TranslationContext.COMMONINVESTIGATION; + } +} diff --git a/frontend/src/app/modules/shared/model/translation-context.model.ts b/frontend/src/app/modules/shared/model/translation-context.model.ts index 2db479e94e..2411ae3e29 100644 --- a/frontend/src/app/modules/shared/model/translation-context.model.ts +++ b/frontend/src/app/modules/shared/model/translation-context.model.ts @@ -17,5 +17,6 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ export enum TranslationContext { - COMMONALERT = 'commonAlert' + COMMONALERT = 'commonAlert', + COMMONINVESTIGATION = 'commonInvestigation' } diff --git a/frontend/src/app/modules/shared/modules/notification/modal/accept/accept-notification-modal.component.html b/frontend/src/app/modules/shared/modules/notification/modal/accept/accept-notification-modal.component.html deleted file mode 100644 index cc20a09582..0000000000 --- a/frontend/src/app/modules/shared/modules/notification/modal/accept/accept-notification-modal.component.html +++ /dev/null @@ -1,28 +0,0 @@ - - - - -
- {{ translationContext + '.modal.acceptReasonHint' | i18n }} - -
-
diff --git a/frontend/src/app/modules/shared/modules/notification/modal/accept/accept-notification-modal.component.spec.ts b/frontend/src/app/modules/shared/modules/notification/modal/accept/accept-notification-modal.component.spec.ts deleted file mode 100644 index 3794fb0dea..0000000000 --- a/frontend/src/app/modules/shared/modules/notification/modal/accept/accept-notification-modal.component.spec.ts +++ /dev/null @@ -1,78 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2022, 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * Copyright (c) 2022, 2023 ZF Friedrichshafen AG - * Copyright (c) 2022, 2023 Contributors to the Eclipse Foundation - * - * See the NOTICE file(s) distributed with this work for additional - * information regarding copyright ownership. - * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0. - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - * - * SPDX-License-Identifier: Apache-2.0 - ********************************************************************************/ - -import { NotificationStatus } from '@shared/model/notification.model'; -import { AcceptNotificationModalComponent } from '@shared/modules/notification/modal/accept/accept-notification-modal.component'; -import { renderAcceptModal } from '@shared/modules/notification/modal/modalTestHelper.spec'; -import { fireEvent, screen, waitFor } from '@testing-library/angular'; -import { getRandomText } from '../../../../../../mocks/services/text-generator.helper'; - -describe('AcceptNotificationModalComponent', () => { - it('should create accept modal', async () => { - await renderAcceptModal(NotificationStatus.ACKNOWLEDGED); - const title = await waitFor(() => screen.getByText('commonAlert.modal.acceptTitle')); - const hint2 = await waitFor(() => screen.getByText('commonAlert.modal.acceptReasonHint')); - const buttonR = await waitFor(() => screen.getByText('actions.accept')); - - expect(title).toBeInTheDocument(); - expect(hint2).toBeInTheDocument(); - expect(buttonR).toBeInTheDocument(); - }); - - it('should render investigation description', async () => { - const { notification } = await renderAcceptModal(NotificationStatus.ACKNOWLEDGED); - const description = await waitFor(() => screen.getByText(notification.description)); - - expect(description).toBeInTheDocument(); - }); - - it('should check validation of textarea', async () => { - await renderAcceptModal(NotificationStatus.ACKNOWLEDGED); - fireEvent.click(await waitFor(() => screen.getByText('actions.accept'))); - - const textArea = await waitFor(() => screen.getByTestId('BaseInputElement-0')); - const errorMessage_1 = await waitFor(() => screen.getByText('errorMessage.required')); - expect(errorMessage_1).toBeInTheDocument(); - - fireEvent.input(textArea, { target: { value: 'Some Text' } }); - const errorMessage_2 = await waitFor(() => screen.getByText('errorMessage.minLength')); - expect(errorMessage_2).toBeInTheDocument(); - - fireEvent.input(textArea, { target: { value: getRandomText(1500) } }); - const errorMessage_3 = await waitFor(() => screen.getByText('errorMessage.maxLength')); - expect(errorMessage_3).toBeInTheDocument(); - - fireEvent.input(textArea, { target: { value: 'Some longer text with at least 15 chars' } }); - expect(errorMessage_1).not.toBeInTheDocument(); - expect(errorMessage_2).not.toBeInTheDocument(); - expect(errorMessage_3).not.toBeInTheDocument(); - }); - - it('should call close function', async () => { - await renderAcceptModal(NotificationStatus.ACKNOWLEDGED); - - const textArea: HTMLTextAreaElement = await waitFor(() => screen.getByTestId('BaseInputElement-0')); - fireEvent.input(textArea, { target: { value: 'Some Text Some Text Some Text' } }); - - fireEvent.click(await waitFor(() => screen.getByText('actions.accept'))); - await waitFor(() => expect(screen.getByText('commonAlert.modal.successfullyAccepted')).toBeInTheDocument()); - }); -}); diff --git a/frontend/src/app/modules/shared/modules/notification/modal/accept/accept-notification-modal.component.ts b/frontend/src/app/modules/shared/modules/notification/modal/accept/accept-notification-modal.component.ts deleted file mode 100644 index 754a937aad..0000000000 --- a/frontend/src/app/modules/shared/modules/notification/modal/accept/accept-notification-modal.component.ts +++ /dev/null @@ -1,82 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2022, 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * Copyright (c) 2022, 2023 ZF Friedrichshafen AG - * Copyright (c) 2022, 2023 Contributors to the Eclipse Foundation - * - * See the NOTICE file(s) distributed with this work for additional - * information regarding copyright ownership. - * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0. - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - * - * SPDX-License-Identifier: Apache-2.0 - ********************************************************************************/ - -import { Component, EventEmitter, Input, Output, TemplateRef, ViewChild } from '@angular/core'; -import { UntypedFormControl, UntypedFormGroup, Validators } from '@angular/forms'; -import { ToastService } from '@shared/components/toasts/toast.service'; -import { Notification } from '@shared/model/notification.model'; -import { TranslationContext } from '@shared/model/translation-context.model'; -import { ModalData } from '@shared/modules/modal/core/modal.model'; -import { ModalService } from '@shared/modules/modal/core/modal.service'; -import { Observable } from 'rxjs'; - -@Component({ - selector: 'app-accept-notification-modal', - templateUrl: './accept-notification-modal.component.html', -}) -export class AcceptNotificationModalComponent { - @ViewChild('Modal') modal: TemplateRef; - @Input() acceptCall: (id: string, reason: string) => Observable; - @Input() translationContext: TranslationContext; - @Output() confirmActionCompleted = new EventEmitter(); - - public notification: Notification; - public readonly formGroup; - private readonly textAreaControl = new UntypedFormControl(); - - constructor(private readonly toastService: ToastService, private readonly confirmModalService: ModalService) { - this.formGroup = new UntypedFormGroup({ reason: this.textAreaControl }); - } - - public show(notification: Notification): void { - this.notification = notification; - - - this.textAreaControl.setValidators([ Validators.required, Validators.maxLength(1000), Validators.minLength(15) ]); - - const onConfirm = (isConfirmed: boolean) => { - const reason = this.formGroup.get('reason').value; - this.formGroup.reset(); - - if (!isConfirmed) return; - - this.acceptCall(notification.id, reason).subscribe({ - next: () => { - this.toastService.success(this.translationContext + '.modal.successfullyAccepted'); - this.confirmActionCompleted.emit(); - }, - error: () => { - this.toastService.error(this.translationContext + '.modal.failedAccept'); - }, - }); - }; - - const options: ModalData = { - title: this.translationContext + '.modal.acceptTitle', - buttonRight: 'actions.accept', - template: this.modal, - formGroup: this.formGroup, - onConfirm, - }; - - this.confirmModalService.open(options); - } -} diff --git a/frontend/src/app/modules/shared/modules/notification/modal/acknowledge/acknowledge-notification-modal.component.html b/frontend/src/app/modules/shared/modules/notification/modal/acknowledge/acknowledge-notification-modal.component.html deleted file mode 100644 index 2c315d6465..0000000000 --- a/frontend/src/app/modules/shared/modules/notification/modal/acknowledge/acknowledge-notification-modal.component.html +++ /dev/null @@ -1,24 +0,0 @@ - - - - - diff --git a/frontend/src/app/modules/shared/modules/notification/modal/acknowledge/acknowledge-notification-modal.component.spec.ts b/frontend/src/app/modules/shared/modules/notification/modal/acknowledge/acknowledge-notification-modal.component.spec.ts deleted file mode 100644 index 55165dc230..0000000000 --- a/frontend/src/app/modules/shared/modules/notification/modal/acknowledge/acknowledge-notification-modal.component.spec.ts +++ /dev/null @@ -1,52 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2022, 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * Copyright (c) 2022, 2023 ZF Friedrichshafen AG - * Copyright (c) 2022, 2023 Contributors to the Eclipse Foundation - * - * See the NOTICE file(s) distributed with this work for additional - * information regarding copyright ownership. - * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0. - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - * - * SPDX-License-Identifier: Apache-2.0 - ********************************************************************************/ - -import { NotificationStatus } from '@shared/model/notification.model'; -import { renderAcknowledgeModal } from '@shared/modules/notification/modal/modalTestHelper.spec'; -import { fireEvent, screen, waitFor } from '@testing-library/angular'; -import { AcknowledgeNotificationModalComponent } from './acknowledge-notification-modal.component'; - -describe('AcknowledgeNotificationModalComponent', () => { - it('should create acknowledge modal', async () => { - await renderAcknowledgeModal(NotificationStatus.RECEIVED); - const title = await waitFor(() => screen.getByText('commonAlert.modal.acknowledgeTitle')); - const buttonR = await waitFor(() => screen.getByText('actions.acknowledge')); - - expect(title).toBeInTheDocument(); - expect(buttonR).toBeInTheDocument(); - }); - - it('should render investigation description', async () => { - const { notification } = await renderAcknowledgeModal(NotificationStatus.RECEIVED); - const description = await waitFor(() => screen.getByText(notification.description)); - - expect(description).toBeInTheDocument(); - }); - - it('should call acknowledge function', async () => { - await renderAcknowledgeModal(NotificationStatus.RECEIVED); - fireEvent.click(await waitFor(() => screen.getByText('actions.acknowledge'))); - - await waitFor(() => - expect(screen.getByText('commonAlert.modal.successfullyAcknowledged')).toBeInTheDocument(), - ); - }); -}); diff --git a/frontend/src/app/modules/shared/modules/notification/modal/acknowledge/acknowledge-notification-modal.component.ts b/frontend/src/app/modules/shared/modules/notification/modal/acknowledge/acknowledge-notification-modal.component.ts deleted file mode 100644 index c1a82ec6e7..0000000000 --- a/frontend/src/app/modules/shared/modules/notification/modal/acknowledge/acknowledge-notification-modal.component.ts +++ /dev/null @@ -1,71 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2022, 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * Copyright (c) 2022, 2023 ZF Friedrichshafen AG - * Copyright (c) 2022, 2023 Contributors to the Eclipse Foundation - * - * See the NOTICE file(s) distributed with this work for additional - * information regarding copyright ownership. - * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0. - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - * - * SPDX-License-Identifier: Apache-2.0 - ********************************************************************************/ - -import { Component, EventEmitter, Input, Output, TemplateRef, ViewChild } from '@angular/core'; -import { ToastService } from '@shared/components/toasts/toast.service'; -import { Notification } from '@shared/model/notification.model'; -import { TranslationContext } from '@shared/model/translation-context.model'; -import { ModalData } from '@shared/modules/modal/core/modal.model'; -import { ModalService } from '@shared/modules/modal/core/modal.service'; -import { Observable } from 'rxjs'; - -@Component({ - selector: 'app-acknowledge-notification-modal', - templateUrl: './acknowledge-notification-modal.component.html', -}) -export class AcknowledgeNotificationModalComponent { - @ViewChild('Modal') modal: TemplateRef; - @Input() acknowledgeCall: (id: string) => Observable; - @Input() translationContext: TranslationContext; - @Output() confirmActionCompleted = new EventEmitter(); - - public notification: Notification; - - constructor(private readonly toastService: ToastService, private readonly confirmModalService: ModalService) { - } - - public show(notification: Notification): void { - this.notification = notification; - const onConfirm = (isConfirmed: boolean) => { - if (!isConfirmed) return; - - this.acknowledgeCall(notification.id).subscribe({ - next: () => { - this.toastService.success(this.translationContext + '.modal.successfullyAcknowledged'); - this.confirmActionCompleted.emit(); - }, - error: () => { - this.toastService.error(this.translationContext + '.modal.failedAcknowledge'); - }, - }); - }; - - const options: ModalData = { - title: this.translationContext + '.modal.acknowledgeTitle', - buttonRight: 'actions.acknowledge', - - template: this.modal, - onConfirm, - }; - - this.confirmModalService.open(options); - } -} diff --git a/frontend/src/app/modules/shared/modules/notification/modal/close/close-notification-modal.component.html b/frontend/src/app/modules/shared/modules/notification/modal/actions/notification-action-modal.component.html similarity index 87% rename from frontend/src/app/modules/shared/modules/notification/modal/close/close-notification-modal.component.html rename to frontend/src/app/modules/shared/modules/notification/modal/actions/notification-action-modal.component.html index 6b53b86df4..2b7ab619c4 100644 --- a/frontend/src/app/modules/shared/modules/notification/modal/close/close-notification-modal.component.html +++ b/frontend/src/app/modules/shared/modules/notification/modal/actions/notification-action-modal.component.html @@ -21,9 +21,8 @@ - -
- {{ translationContext + '.modal.closeReasonHint' | i18n }} + + {{ reasonHintLabel | i18n }}
-
+ diff --git a/frontend/src/app/modules/shared/modules/notification/modal/actions/notification-action-modal.component.ts b/frontend/src/app/modules/shared/modules/notification/modal/actions/notification-action-modal.component.ts new file mode 100644 index 0000000000..e06c0ddb5e --- /dev/null +++ b/frontend/src/app/modules/shared/modules/notification/modal/actions/notification-action-modal.component.ts @@ -0,0 +1,173 @@ +/******************************************************************************** + * Copyright (c) 2022, 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * Copyright (c) 2022, 2023 ZF Friedrichshafen AG + * Copyright (c) 2022, 2023 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +import { Component, EventEmitter, Input, Output, TemplateRef, ViewChild } from '@angular/core'; +import { UntypedFormControl, UntypedFormGroup, Validators } from '@angular/forms'; +import { ToastService } from '@shared/components/toasts/toast.service'; +import { getTranslationContext } from '@shared/helper/notification-helper'; +import { Notification, NotificationStatus } from '@shared/model/notification.model'; +import { TranslationContext } from '@shared/model/translation-context.model'; +import { ModalData } from '@shared/modules/modal/core/modal.model'; +import { ModalService } from '@shared/modules/modal/core/modal.service'; +import { Observable } from 'rxjs'; + +@Component({ + selector: 'app-notification-action-modal', + templateUrl: './notification-action-modal.component.html', +}) + +export class NotificationActionModalComponent { + @ViewChild('Modal') modal: TemplateRef; + @Input() callback: (status: NotificationStatus, id: string, reason?: string) => Observable; + @Output() confirmActionCompleted = new EventEmitter(); + + public showTextArea = false; + public notification: Notification; + public readonly formGroup; + private readonly textAreaControl = new UntypedFormControl(); + public reasonHintLabel = null; + + constructor(private readonly toastService: ToastService, private readonly confirmModalService: ModalService) { + this.formGroup = new UntypedFormGroup({ reason: this.textAreaControl }); + } + + getModalDataBasedOnNotificationStatus(desiredStatus: NotificationStatus): NotificationModalData { + const context = getTranslationContext(this.notification); + switch (desiredStatus) { + case NotificationStatus.ACCEPTED: { + this.reasonHintLabel = context + '.modal.acceptReasonHint'; + return { + title: context + '.modal.acceptTitle', + buttonRight: 'actions.accept', + successMessage: context + '.modal.successfullyAccepted', + errorMessage: context + '.modal.failedAccept', + reasonHint: context + '.modal.acceptReasonHint' + }; + } + case NotificationStatus.DECLINED: { + this.reasonHintLabel = context + '.modal.declineReasonHint'; + + return { + title: context + '.modal.declineTitle', + buttonRight: 'actions.decline', + successMessage: context + '.modal.successfullyDeclined', + errorMessage: context + '.modal.failedDecline', + reasonHint: context + '.modal.declineReasonHint' + }; + } + case NotificationStatus.ACKNOWLEDGED: { + return { + title: context + '.modal.acknowledgeTitle', + buttonRight: 'actions.acknowledge', + successMessage: context + '.modal.successfullyAcknowledged', + errorMessage: context + '.modal.failedAcknowledge', + }; + } + case NotificationStatus.APPROVED: { + return { + title: context + '.modal.approvalTitle', + buttonRight: 'actions.confirm', + successMessage: context + '.modal.successfullyApproved', + errorMessage: context + '.modal.failedApprove', + }; + } + case NotificationStatus.CANCELED: { + return { + title: context + '.modal.cancellationTitle', + buttonRight: 'actions.cancellationConfirm', + successMessage: context + '.modal.successfullyCanceled', + errorMessage: context + '.modal.failedCancel', + }; + } + case NotificationStatus.CLOSED: { + this.reasonHintLabel = context + '.modal.closeReasonHint'; + return { + title: context + '.modal.closeTitle', + buttonRight: 'actions.close', + successMessage: context + '.modal.successfullyClosed', + errorMessage: context + '.modal.failedClose', + reasonHint: context + '.modal.closeReasonHint' + }; + } + } + + } + + public show(notification: Notification, desiredStatus: NotificationStatus): void { + this.notification = notification; + const modalData = this.getModalDataBasedOnNotificationStatus(desiredStatus); + + if (this.hasTextArea(desiredStatus)){ + this.showTextArea = true; + this.textAreaControl.setValidators([ Validators.required, Validators.maxLength(1000), Validators.minLength(15) ]); + this.formGroup.reset(); + } else { + this.showTextArea = false; + } + const onConfirm = (isConfirmed: boolean) => { + if (!isConfirmed) return; + + const reason = this.formGroup.get('reason').value; + this.callback(desiredStatus, notification.id, reason).subscribe({ + next: () => { + this.toastService.success(modalData.successMessage); + this.confirmActionCompleted.emit(); + }, + error: () => { + this.toastService.error(modalData.errorMessage, 15000, true); + }, + }); + }; + + const options: ModalData = { + title: modalData.title, + buttonRight: modalData.buttonRight, + template: this.modal, + onConfirm, + }; + + if (desiredStatus === NotificationStatus.CANCELED) { + options.primaryButtonColour = 'warn'; + options.notificationId = this.notification.id; + options.type = getTranslationContext(this.notification) + '.modal.cancellationConfirmationLabel'; + } + if (this.hasTextArea(desiredStatus)){ + options.formGroup = this.formGroup; + } + + this.confirmModalService.open(options); + } + + + private hasTextArea(desiredStatus: NotificationStatus) { + console.log(desiredStatus, "desired from textarea"); + return desiredStatus === NotificationStatus.CLOSED || desiredStatus === NotificationStatus.ACCEPTED || desiredStatus === NotificationStatus.DECLINED; + } +} + +export interface NotificationModalData { + title: string, + buttonRight: string, + successMessage: string, + errorMessage: string, + reason?: string + reasonHint?: string +} diff --git a/frontend/src/app/modules/shared/modules/notification/modal/approve/approve-notification-modal.component.html b/frontend/src/app/modules/shared/modules/notification/modal/approve/approve-notification-modal.component.html deleted file mode 100644 index 2c315d6465..0000000000 --- a/frontend/src/app/modules/shared/modules/notification/modal/approve/approve-notification-modal.component.html +++ /dev/null @@ -1,24 +0,0 @@ - - - - - diff --git a/frontend/src/app/modules/shared/modules/notification/modal/approve/approve-notification-modal.component.spec.ts b/frontend/src/app/modules/shared/modules/notification/modal/approve/approve-notification-modal.component.spec.ts deleted file mode 100644 index 49df4ff40e..0000000000 --- a/frontend/src/app/modules/shared/modules/notification/modal/approve/approve-notification-modal.component.spec.ts +++ /dev/null @@ -1,50 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2022, 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * Copyright (c) 2022, 2023 ZF Friedrichshafen AG - * Copyright (c) 2022, 2023 Contributors to the Eclipse Foundation - * - * See the NOTICE file(s) distributed with this work for additional - * information regarding copyright ownership. - * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0. - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - * - * SPDX-License-Identifier: Apache-2.0 - ********************************************************************************/ - -import { NotificationStatus } from '@shared/model/notification.model'; -import { renderApproveModal } from '@shared/modules/notification/modal/modalTestHelper.spec'; -import { fireEvent, screen, waitFor } from '@testing-library/angular'; -import { ApproveNotificationModalComponent } from './approve-notification-modal.component'; - -describe('ApproveNotificationModalComponent', () => { - it('should create approve modal', async () => { - await renderApproveModal(NotificationStatus.CREATED); - const title = await waitFor(() => screen.getByText('commonAlert.modal.approvalTitle')); - const buttonR = await waitFor(() => screen.getByText('actions.confirm')); - - expect(title).toBeInTheDocument(); - expect(buttonR).toBeInTheDocument(); - }); - - it('should render investigation description', async () => { - const { notification } = await renderApproveModal(NotificationStatus.CREATED); - const description = await waitFor(() => screen.getByText(notification.description)); - - expect(description).toBeInTheDocument(); - }); - - it('should call approve function', async () => { - await renderApproveModal(NotificationStatus.CREATED); - fireEvent.click(await waitFor(() => screen.getByText('actions.confirm'))); - - await waitFor(() => expect(screen.getByText('commonAlert.modal.successfullyApproved')).toBeInTheDocument()); - }); -}); diff --git a/frontend/src/app/modules/shared/modules/notification/modal/approve/approve-notification-modal.component.ts b/frontend/src/app/modules/shared/modules/notification/modal/approve/approve-notification-modal.component.ts deleted file mode 100644 index 0f21b9cf63..0000000000 --- a/frontend/src/app/modules/shared/modules/notification/modal/approve/approve-notification-modal.component.ts +++ /dev/null @@ -1,72 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2022, 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * Copyright (c) 2022, 2023 ZF Friedrichshafen AG - * Copyright (c) 2022, 2023 Contributors to the Eclipse Foundation - * - * See the NOTICE file(s) distributed with this work for additional - * information regarding copyright ownership. - * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0. - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - * - * SPDX-License-Identifier: Apache-2.0 - ********************************************************************************/ - -import { Component, EventEmitter, Input, Output, TemplateRef, ViewChild } from '@angular/core'; -import { ToastService } from '@shared/components/toasts/toast.service'; -import { Notification } from '@shared/model/notification.model'; -import { TranslationContext } from '@shared/model/translation-context.model'; -import { ModalData } from '@shared/modules/modal/core/modal.model'; -import { ModalService } from '@shared/modules/modal/core/modal.service'; -import { Observable } from 'rxjs'; - -@Component({ - selector: 'app-approve-notification-modal', - templateUrl: './approve-notification-modal.component.html', -}) -export class ApproveNotificationModalComponent { - @ViewChild('Modal') modal: TemplateRef; - @Input() approveCall: (id: string) => Observable; - @Input() translationContext: TranslationContext; - @Output() confirmActionCompleted = new EventEmitter(); - - public notification: Notification; - - constructor(private readonly toastService: ToastService, private readonly confirmModalService: ModalService) { - this.toastService.retryAction.subscribe(() => this.show(this.notification)) - } - - public show(notification: Notification): void { - this.notification = notification; - const onConfirm = (isConfirmed: boolean) => { - if (!isConfirmed) return; - - this.approveCall(notification.id).subscribe({ - next: () => { - this.toastService.success(this.translationContext + '.modal.successfullyApproved'); - this.confirmActionCompleted.emit(); - }, - error: (err) => { - this.toastService.error(this.translationContext + '.modal.failedApprove', 15000,true); - }, - }); - }; - - const options: ModalData = { - title: this.translationContext + '.modal.approvalTitle', - buttonRight: 'actions.confirm', - - template: this.modal, - onConfirm, - }; - - this.confirmModalService.open(options); - } -} diff --git a/frontend/src/app/modules/shared/modules/notification/modal/cancel/cancel-notification-modal.component.html b/frontend/src/app/modules/shared/modules/notification/modal/cancel/cancel-notification-modal.component.html deleted file mode 100644 index 2c315d6465..0000000000 --- a/frontend/src/app/modules/shared/modules/notification/modal/cancel/cancel-notification-modal.component.html +++ /dev/null @@ -1,24 +0,0 @@ - - - - - diff --git a/frontend/src/app/modules/shared/modules/notification/modal/cancel/cancel-notification-modal.component.spec.ts b/frontend/src/app/modules/shared/modules/notification/modal/cancel/cancel-notification-modal.component.spec.ts deleted file mode 100644 index db47e2749a..0000000000 --- a/frontend/src/app/modules/shared/modules/notification/modal/cancel/cancel-notification-modal.component.spec.ts +++ /dev/null @@ -1,43 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2022, 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * Copyright (c) 2022, 2023 ZF Friedrichshafen AG - * Copyright (c) 2022, 2023 Contributors to the Eclipse Foundation - * - * See the NOTICE file(s) distributed with this work for additional - * information regarding copyright ownership. - * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0. - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - * - * SPDX-License-Identifier: Apache-2.0 - ********************************************************************************/ - -import { NotificationStatus } from '@shared/model/notification.model'; -import { CancelNotificationModalComponent } from '@shared/modules/notification/modal/cancel/cancel-notification-modal.component'; -import { renderCancelModal } from '@shared/modules/notification/modal/modalTestHelper.spec'; -import { screen, waitFor } from '@testing-library/angular'; - -describe('CancelNotificationModalComponent', () => { - it('should create cancel modal', async () => { - await renderCancelModal(NotificationStatus.CREATED); - const title = await waitFor(() => screen.getByText('commonAlert.modal.cancellationTitle')); - const buttonR = await waitFor(() => screen.getByText('actions.cancellationConfirm')); - - expect(title).toBeInTheDocument(); - expect(buttonR).toBeInTheDocument(); - }); - - it('should render investigation description', async () => { - const { notification } = await renderCancelModal(NotificationStatus.CREATED); - const description = await waitFor(() => screen.getByText(notification.description)); - - expect(description).toBeInTheDocument(); - }); -}); diff --git a/frontend/src/app/modules/shared/modules/notification/modal/cancel/cancel-notification-modal.component.ts b/frontend/src/app/modules/shared/modules/notification/modal/cancel/cancel-notification-modal.component.ts deleted file mode 100644 index de0b9ce3b1..0000000000 --- a/frontend/src/app/modules/shared/modules/notification/modal/cancel/cancel-notification-modal.component.ts +++ /dev/null @@ -1,76 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2022, 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * Copyright (c) 2022, 2023 ZF Friedrichshafen AG - * Copyright (c) 2022, 2023 Contributors to the Eclipse Foundation - * - * See the NOTICE file(s) distributed with this work for additional - * information regarding copyright ownership. - * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0. - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - * - * SPDX-License-Identifier: Apache-2.0 - ********************************************************************************/ - -import { Component, EventEmitter, Input, Output, TemplateRef, ViewChild } from '@angular/core'; -import { ToastService } from '@shared/components/toasts/toast.service'; -import { Notification } from '@shared/model/notification.model'; -import { TranslationContext } from '@shared/model/translation-context.model'; -import { ModalData } from '@shared/modules/modal/core/modal.model'; -import { ModalService } from '@shared/modules/modal/core/modal.service'; -import { Observable } from 'rxjs'; - -@Component({ - selector: 'app-cancel-notification-modal', - templateUrl: './cancel-notification-modal.component.html', -}) -export class CancelNotificationModalComponent { - @ViewChild('Modal') modal: TemplateRef; - @Input() cancelCall: (id: string) => Observable; - @Input() translationContext: TranslationContext; - @Output() confirmActionCompleted = new EventEmitter(); - - - public notification: Notification; - - constructor(private readonly toastService: ToastService, private readonly confirmModalService: ModalService) { - } - - public show(notification: Notification): void { - this.notification = notification; - - const onConfirm = (isConfirmed: boolean) => { - - if (!isConfirmed) return; - - this.cancelCall(notification.id).subscribe({ - next: () => { - this.toastService.success(this.translationContext + '.modal.successfullyCanceled'); - this.confirmActionCompleted.emit(); - }, - error: () => { - this.toastService.error(this.translationContext + '.modal.failedCancel'); - }, - }); - }; - - const options: ModalData = { - title: this.translationContext + '.modal.cancellationTitle', - type: this.translationContext + '.modal.cancellationConfirmationLabel', - buttonRight: 'actions.cancellationConfirm', - primaryButtonColour: 'warn', - notificationId: this.notification.id, - template: this.modal, - onConfirm, - }; - - this.confirmModalService.open(options); - } -} diff --git a/frontend/src/app/modules/shared/modules/notification/modal/close/close-notification-modal.component.spec.ts b/frontend/src/app/modules/shared/modules/notification/modal/close/close-notification-modal.component.spec.ts deleted file mode 100644 index 282aeee0e2..0000000000 --- a/frontend/src/app/modules/shared/modules/notification/modal/close/close-notification-modal.component.spec.ts +++ /dev/null @@ -1,78 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2022, 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * Copyright (c) 2022, 2023 ZF Friedrichshafen AG - * Copyright (c) 2022, 2023 Contributors to the Eclipse Foundation - * - * See the NOTICE file(s) distributed with this work for additional - * information regarding copyright ownership. - * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0. - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - * - * SPDX-License-Identifier: Apache-2.0 - ********************************************************************************/ - -import { NotificationStatus } from '@shared/model/notification.model'; -import { CloseNotificationModalComponent } from '@shared/modules/notification/modal/close/close-notification-modal.component'; -import { renderCloseModal } from '@shared/modules/notification/modal/modalTestHelper.spec'; -import { fireEvent, screen, waitFor } from '@testing-library/angular'; -import { getRandomText } from '../../../../../../mocks/services/text-generator.helper'; - -describe('CloseNotificationModalComponent', () => { - it('should create close modal', async () => { - await renderCloseModal(NotificationStatus.SENT); - const title = await waitFor(() => screen.getByText('commonAlert.modal.closeTitle')); - const hint2 = await waitFor(() => screen.getByText('commonAlert.modal.closeReasonHint')); - const buttonR = await waitFor(() => screen.getByText('actions.close')); - - expect(title).toBeInTheDocument(); - expect(hint2).toBeInTheDocument(); - expect(buttonR).toBeInTheDocument(); - }); - - it('should render investigation description', async () => { - const { notification } = await renderCloseModal(NotificationStatus.SENT); - const description = await waitFor(() => screen.getByText(notification.description)); - - expect(description).toBeInTheDocument(); - }); - - it('should check validation of textarea', async () => { - await renderCloseModal(NotificationStatus.SENT); - fireEvent.click(await waitFor(() => screen.getByText('actions.close'))); - const textArea: HTMLTextAreaElement = await waitFor(() => screen.getByTestId('BaseInputElement-0')); - - const errorMessage_1 = await waitFor(() => screen.getByText('errorMessage.required')); - expect(errorMessage_1).toBeInTheDocument(); - - fireEvent.input(textArea, { target: { value: 'Some Text' } }); - const errorMessage_2 = await waitFor(() => screen.getByText('errorMessage.minLength')); - expect(errorMessage_2).toBeInTheDocument(); - - fireEvent.input(textArea, { target: { value: getRandomText(1500) } }); - const errorMessage_3 = await waitFor(() => screen.getByText('errorMessage.maxLength')); - expect(errorMessage_3).toBeInTheDocument(); - - fireEvent.input(textArea, { target: { value: 'Some longer text with at least 15 chars' } }); - expect(errorMessage_1).not.toBeInTheDocument(); - expect(errorMessage_2).not.toBeInTheDocument(); - expect(errorMessage_3).not.toBeInTheDocument(); - }); - - it('should call close function', async () => { - await renderCloseModal(NotificationStatus.SENT); - - const textArea = await waitFor(() => screen.getByTestId('BaseInputElement-0')); - fireEvent.input(textArea, { target: { value: 'Some Text Some Text Some Text' } }); - - fireEvent.click(await waitFor(() => screen.getByText('actions.close'))); - await waitFor(() => expect(screen.getByText('commonAlert.modal.successfullyClosed')).toBeInTheDocument()); - }); -}); diff --git a/frontend/src/app/modules/shared/modules/notification/modal/close/close-notification-modal.component.ts b/frontend/src/app/modules/shared/modules/notification/modal/close/close-notification-modal.component.ts deleted file mode 100644 index 66b121d5b6..0000000000 --- a/frontend/src/app/modules/shared/modules/notification/modal/close/close-notification-modal.component.ts +++ /dev/null @@ -1,81 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2022, 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * Copyright (c) 2022, 2023 ZF Friedrichshafen AG - * Copyright (c) 2022, 2023 Contributors to the Eclipse Foundation - * - * See the NOTICE file(s) distributed with this work for additional - * information regarding copyright ownership. - * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0. - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - * - * SPDX-License-Identifier: Apache-2.0 - ********************************************************************************/ - -import { Component, EventEmitter, Input, Output, TemplateRef, ViewChild } from '@angular/core'; -import { UntypedFormControl, UntypedFormGroup, Validators } from '@angular/forms'; -import { ToastService } from '@shared/components/toasts/toast.service'; -import { Notification } from '@shared/model/notification.model'; -import { TranslationContext } from '@shared/model/translation-context.model'; -import { ModalData } from '@shared/modules/modal/core/modal.model'; -import { ModalService } from '@shared/modules/modal/core/modal.service'; -import { Observable } from 'rxjs'; - -@Component({ - selector: 'app-close-notification-modal', - templateUrl: './close-notification-modal.component.html', -}) -export class CloseNotificationModalComponent { - @ViewChild('Modal') modal: TemplateRef; - @Input() closeCall: (id: string, reason: string) => Observable; - @Input() translationContext: TranslationContext; - @Output() confirmActionCompleted = new EventEmitter(); - - public notification: Notification; - public readonly formGroup; - private readonly textAreaControl = new UntypedFormControl(); - - constructor(private readonly toastService: ToastService, private readonly confirmModalService: ModalService) { - this.formGroup = new UntypedFormGroup({ reason: this.textAreaControl }); - } - - public show(notification: Notification): void { - this.notification = notification; - this.textAreaControl.setValidators([ Validators.required, Validators.maxLength(1000), Validators.minLength(15) ]); - - const onConfirm = (isConfirmed: boolean) => { - const reason = this.formGroup.get('reason').value; - this.formGroup.reset(); - - if (!isConfirmed) return; - - this.closeCall(notification.id, reason).subscribe({ - next: () => { - this.toastService.success(this.translationContext + '.modal.successfullyClosed'); - this.confirmActionCompleted.emit(); - }, - error: () => { - this.toastService.error(this.translationContext + '.modal.failedClose'); - }, - }); - }; - - const options: ModalData = { - title: this.translationContext + '.modal.closeTitle', - buttonRight: 'actions.close', - - template: this.modal, - formGroup: this.formGroup, - onConfirm, - }; - - this.confirmModalService.open(options); - } -} diff --git a/frontend/src/app/modules/shared/modules/notification/modal/decline/decline-notification-modal.component.html b/frontend/src/app/modules/shared/modules/notification/modal/decline/decline-notification-modal.component.html deleted file mode 100644 index 64e86996c0..0000000000 --- a/frontend/src/app/modules/shared/modules/notification/modal/decline/decline-notification-modal.component.html +++ /dev/null @@ -1,29 +0,0 @@ - - - - - -
- {{ translationContext + '.modal.declineReasonHint' | i18n }} - -
-
diff --git a/frontend/src/app/modules/shared/modules/notification/modal/decline/decline-notification-modal.component.spec.ts b/frontend/src/app/modules/shared/modules/notification/modal/decline/decline-notification-modal.component.spec.ts deleted file mode 100644 index 252d130a0a..0000000000 --- a/frontend/src/app/modules/shared/modules/notification/modal/decline/decline-notification-modal.component.spec.ts +++ /dev/null @@ -1,78 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2022, 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * Copyright (c) 2022, 2023 ZF Friedrichshafen AG - * Copyright (c) 2022, 2023 Contributors to the Eclipse Foundation - * - * See the NOTICE file(s) distributed with this work for additional - * information regarding copyright ownership. - * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0. - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - * - * SPDX-License-Identifier: Apache-2.0 - ********************************************************************************/ - -import { NotificationStatus } from '@shared/model/notification.model'; -import { renderDeclineModal } from '@shared/modules/notification/modal/modalTestHelper.spec'; -import { fireEvent, screen, waitFor } from '@testing-library/angular'; -import { getRandomText } from '../../../../../../mocks/services/text-generator.helper'; - -describe('DeclineNotificationModalComponent', () => { - it('should create close modal', async () => { - await renderDeclineModal(NotificationStatus.ACKNOWLEDGED); - const title = await waitFor(() => screen.getByText('commonAlert.modal.declineTitle')); - const hint2 = await waitFor(() => screen.getByText('commonAlert.modal.declineReasonHint')); - const buttonR = await waitFor(() => screen.getByText('actions.decline')); - - expect(title).toBeInTheDocument(); - expect(hint2).toBeInTheDocument(); - expect(buttonR).toBeInTheDocument(); - }); - - it('should render investigation description', async () => { - const { notification } = await renderDeclineModal(NotificationStatus.ACKNOWLEDGED); - const description = await waitFor(() => screen.getByText(notification.description)); - - expect(description).toBeInTheDocument(); - }); - - it('should check validation of textarea', async () => { - await renderDeclineModal(NotificationStatus.ACKNOWLEDGED); - fireEvent.click(await waitFor(() => screen.getByText('actions.decline'))); - - const textArea: HTMLTextAreaElement = await waitFor(() => screen.getByTestId('BaseInputElement-0')); - - const errorMessage_1 = await waitFor(() => screen.getByText('errorMessage.required')); - expect(errorMessage_1).toBeInTheDocument(); - - fireEvent.input(textArea, { target: { value: 'Some Text' } }); - const errorMessage_2 = await waitFor(() => screen.getByText('errorMessage.minLength')); - expect(errorMessage_2).toBeInTheDocument(); - - fireEvent.input(textArea, { target: { value: getRandomText(1500) } }); - const errorMessage_3 = await waitFor(() => screen.getByText('errorMessage.maxLength')); - expect(errorMessage_3).toBeInTheDocument(); - - fireEvent.input(textArea, { target: { value: 'Some longer text with at least 15 chars' } }); - expect(errorMessage_1).not.toBeInTheDocument(); - expect(errorMessage_2).not.toBeInTheDocument(); - expect(errorMessage_3).not.toBeInTheDocument(); - }); - - it('should call close function', async () => { - await renderDeclineModal(NotificationStatus.ACKNOWLEDGED); - - const textArea: HTMLTextAreaElement = await waitFor(() => screen.getByTestId('BaseInputElement-0')); - fireEvent.input(textArea, { target: { value: 'Some Text Some Text Some Text' } }); - fireEvent.click(await waitFor(() => screen.getByText('actions.decline'))); - - await waitFor(() => expect(screen.getByText('commonAlert.modal.successfullyDeclined')).toBeInTheDocument()); - }); -}); diff --git a/frontend/src/app/modules/shared/modules/notification/modal/decline/decline-notification-modal.component.ts b/frontend/src/app/modules/shared/modules/notification/modal/decline/decline-notification-modal.component.ts deleted file mode 100644 index 48d37b730c..0000000000 --- a/frontend/src/app/modules/shared/modules/notification/modal/decline/decline-notification-modal.component.ts +++ /dev/null @@ -1,81 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2022, 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * Copyright (c) 2022, 2023 ZF Friedrichshafen AG - * Copyright (c) 2022, 2023 Contributors to the Eclipse Foundation - * - * See the NOTICE file(s) distributed with this work for additional - * information regarding copyright ownership. - * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0. - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - * - * SPDX-License-Identifier: Apache-2.0 - ********************************************************************************/ - -import { Component, EventEmitter, Input, Output, TemplateRef, ViewChild } from '@angular/core'; -import { UntypedFormControl, UntypedFormGroup, Validators } from '@angular/forms'; -import { ToastService } from '@shared/components/toasts/toast.service'; -import { Notification } from '@shared/model/notification.model'; -import { TranslationContext } from '@shared/model/translation-context.model'; -import { ModalData } from '@shared/modules/modal/core/modal.model'; -import { ModalService } from '@shared/modules/modal/core/modal.service'; -import { Observable } from 'rxjs'; - -@Component({ - selector: 'app-decline-notification-modal', - templateUrl: './decline-notification-modal.component.html', -}) -export class DeclineNotificationModalComponent { - @ViewChild('Modal') modal: TemplateRef; - @Input() declineCall: (id: string, reason: string) => Observable; - @Input() translationContext: TranslationContext; - @Output() confirmActionCompleted = new EventEmitter(); - - public notification: Notification; - public readonly formGroup; - private readonly textAreaControl = new UntypedFormControl(); - - constructor(private readonly toastService: ToastService, private readonly confirmModalService: ModalService) { - this.formGroup = new UntypedFormGroup({ reason: this.textAreaControl }); - } - - public show(notification: Notification): void { - this.notification = notification; - this.textAreaControl.setValidators([ Validators.required, Validators.maxLength(1000), Validators.minLength(15) ]); - - const onConfirm = (isConfirmed: boolean) => { - const reason = this.formGroup.get('reason').value; - this.formGroup.reset(); - - if (!isConfirmed) return; - - this.declineCall(notification.id, reason).subscribe({ - next: () => { - this.toastService.success(this.translationContext + '.modal.successfullyDeclined'); - this.confirmActionCompleted.emit(); - }, - error: () => { - this.toastService.error(this.translationContext + '.modal.failedDecline'); - }, - }); - }; - - const options: ModalData = { - title: this.translationContext + '.modal.declineTitle', - buttonRight: 'actions.decline', - - template: this.modal, - formGroup: this.formGroup, - onConfirm, - }; - - this.confirmModalService.open(options); - } -} diff --git a/frontend/src/app/modules/shared/modules/notification/modal/modalTestHelper.spec.ts b/frontend/src/app/modules/shared/modules/notification/modal/modalTestHelper.spec.ts deleted file mode 100644 index 57e218e763..0000000000 --- a/frontend/src/app/modules/shared/modules/notification/modal/modalTestHelper.spec.ts +++ /dev/null @@ -1,221 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2022, 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * Copyright (c) 2022, 2023 ZF Friedrichshafen AG - * Copyright (c) 2022, 2023 Contributors to the Eclipse Foundation - * - * See the NOTICE file(s) distributed with this work for additional - * information regarding copyright ownership. - * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0. - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - * - * SPDX-License-Identifier: Apache-2.0 - ********************************************************************************/ - -import { AfterViewInit, Component, Input, ViewChild } from '@angular/core'; -import { CalendarDateModel } from '@core/model/calendar-date.model'; -import { Notification, NotificationStatus } from '@shared/model/notification.model'; -import { Severity } from '@shared/model/severity.model'; -import { TranslationContext } from '@shared/model/translation-context.model'; -import { AcceptNotificationModalComponent } from '@shared/modules/notification/modal/accept/accept-notification-modal.component'; -import { AcknowledgeNotificationModalComponent } from '@shared/modules/notification/modal/acknowledge/acknowledge-notification-modal.component'; -import { ApproveNotificationModalComponent } from '@shared/modules/notification/modal/approve/approve-notification-modal.component'; -import { CancelNotificationModalComponent } from '@shared/modules/notification/modal/cancel/cancel-notification-modal.component'; -import { CloseNotificationModalComponent } from '@shared/modules/notification/modal/close/close-notification-modal.component'; -import { DeclineNotificationModalComponent } from '@shared/modules/notification/modal/decline/decline-notification-modal.component'; -import { NotificationModule } from '@shared/modules/notification/notification.module'; -import { SharedModule } from '@shared/shared.module'; -import { TemplateModule } from '@shared/template.module'; -import { renderComponent } from '@tests/test-render.utils'; -import { of } from 'rxjs'; - -@Component({ - selector: '', - template: - '', -}) -class AcceptModalComponent implements AfterViewInit { - @ViewChild(AcceptNotificationModalComponent) modal: AcceptNotificationModalComponent; - @Input() notification: Notification; - public call = (id: string) => of(null); - - public ngAfterViewInit() { - this.modal.show(this.notification); - } - - protected readonly TranslationContext = TranslationContext; -} - -@Component({ - selector: '', - template: - '', -}) -class AcknowledgeModalComponent implements AfterViewInit { - @ViewChild(AcknowledgeNotificationModalComponent) modal: AcknowledgeNotificationModalComponent; - @Input() notification: Notification; - public call = (id: string) => of(null); - - public ngAfterViewInit() { - this.modal.show(this.notification); - } - - protected readonly TranslationContext = TranslationContext; -} - -@Component({ - selector: '', - template: - '', -}) -class ApproveModalComponent implements AfterViewInit { - @ViewChild(ApproveNotificationModalComponent) modal: ApproveNotificationModalComponent; - @Input() notification: Notification; - public call = (id: string) => of(null); - - public ngAfterViewInit() { - this.modal.show(this.notification); - } - - protected readonly TranslationContext = TranslationContext; -} - -@Component({ - selector: '', - template: - '', -}) -class CancelModalComponent implements AfterViewInit { - @ViewChild(CancelNotificationModalComponent) modal: CancelNotificationModalComponent; - @Input() notification: Notification; - public call = (id: string) => of(null); - - public ngAfterViewInit() { - this.modal.show(this.notification); - } - - protected readonly TranslationContext = TranslationContext; -} - -@Component({ - selector: '', - template: - '', -}) -class DeclineModalComponent implements AfterViewInit { - @ViewChild(DeclineNotificationModalComponent) modal: DeclineNotificationModalComponent; - @Input() notification: Notification; - public call = (id: string) => of(null); - - public ngAfterViewInit() { - this.modal.show(this.notification); - } - - protected readonly TranslationContext = TranslationContext; -} - -@Component({ - selector: '', - template: - '', -}) -class CloseModalComponent implements AfterViewInit { - @ViewChild(CloseNotificationModalComponent) modal: CloseNotificationModalComponent; - @Input() notification: Notification; - public call = (id: string) => of(null); - - public ngAfterViewInit() { - this.modal.show(this.notification); - } - - protected readonly TranslationContext = TranslationContext; -} - -export const notificationTemplate: Notification = { - id: 'id-1', - description: 'Investigation No 1', - createdBy: 'BPNA', - title: 'Title', - createdByName: 'CompanyA', - sendTo: 'BPNB', - sendToName: 'CompanyB', - reason: { close: '', accept: '', decline: '' }, - isFromSender: false, - assetIds: [ 'MOCK_part_1' ], - status: null, - severity: Severity.MINOR, - createdDate: new CalendarDateModel('2022-05-01T10:34:12.000Z'), -}; - -export const renderAcceptModal = async (status: NotificationStatus) => { - const notification = { ...notificationTemplate, status }; - const { fixture } = await renderComponent(AcceptModalComponent, { - declarations: [ AcceptModalComponent ], - imports: [ NotificationModule, SharedModule, TemplateModule ], - componentProperties: { notification }, - }); - - return { fixture, notification }; -}; - -export const renderAcknowledgeModal = async (status: NotificationStatus) => { - const notification = { ...notificationTemplate, status }; - const { fixture } = await renderComponent(AcknowledgeModalComponent, { - declarations: [ AcknowledgeModalComponent ], - imports: [ NotificationModule, SharedModule, TemplateModule ], - componentProperties: { notification }, - }); - - return { fixture, notification }; -}; - -export const renderApproveModal = async (status: NotificationStatus) => { - const notification = { ...notificationTemplate, status }; - const { fixture } = await renderComponent(ApproveModalComponent, { - declarations: [ ApproveModalComponent ], - imports: [ NotificationModule, SharedModule, TemplateModule ], - componentProperties: { notification }, - }); - - return { fixture, notification }; -}; - -export const renderCancelModal = async (status: NotificationStatus) => { - const notification = { ...notificationTemplate, status }; - const { fixture } = await renderComponent(CancelModalComponent, { - declarations: [ CancelModalComponent ], - imports: [ NotificationModule, SharedModule, TemplateModule ], - componentProperties: { notification }, - }); - - return { fixture, notification }; -}; - -export const renderCloseModal = async (status: NotificationStatus) => { - const notification = { ...notificationTemplate, status }; - const { fixture } = await renderComponent(CloseModalComponent, { - declarations: [ CloseModalComponent ], - imports: [ NotificationModule, SharedModule, TemplateModule ], - componentProperties: { notification }, - }); - - return { fixture, notification }; -}; - -export const renderDeclineModal = async (status: NotificationStatus) => { - const notification = { ...notificationTemplate, status }; - const { fixture } = await renderComponent(DeclineModalComponent, { - declarations: [ DeclineModalComponent ], - imports: [ NotificationModule, SharedModule, TemplateModule ], - componentProperties: { notification }, - }); - - return { fixture, notification }; -}; diff --git a/frontend/src/app/modules/shared/modules/notification/notification-tab/notification-tab.component.html b/frontend/src/app/modules/shared/modules/notification/notification-tab/notification-tab.component.html index eaa1a90900..bd9d7028b4 100644 --- a/frontend/src/app/modules/shared/modules/notification/notification-tab/notification-tab.component.html +++ b/frontend/src/app/modules/shared/modules/notification/notification-tab/notification-tab.component.html @@ -27,7 +27,7 @@ [paginationData]="notifications.data" [additionalTableHeader]="true" [autocompleteEnabled]="autocompleteEnabled" - [tableHeader]="(notificationType === NotificationType.INVESTIGATION) ? ('pageTitle.investigations') : ('pageTitle.alerts')" + [tableHeader]="('pageTitle.alerts')" [tableConfig]="tableConfig" [noShadow]="true" [labelId]="labelId" diff --git a/frontend/src/app/modules/shared/modules/notification/notification-tab/notification-tab.component.ts b/frontend/src/app/modules/shared/modules/notification/notification-tab/notification-tab.component.ts index f14f4b82d3..81d579eb77 100644 --- a/frontend/src/app/modules/shared/modules/notification/notification-tab/notification-tab.component.ts +++ b/frontend/src/app/modules/shared/modules/notification/notification-tab/notification-tab.component.ts @@ -47,13 +47,11 @@ export class NotificationTabComponent implements AfterViewInit { @Input() optionalColumns: Array<'title'|'targetDate' | 'severity' | 'createdBy' | 'sendTo' | 'sendToName' | 'createdByName' | 'type'> = []; @Input() sortableColumns: Record = {}; @Input() multiSortList: TableHeaderSort[] = []; - @Input() notificationType = NotificationType.INVESTIGATION; @Input() tableType: TableType; @Input() autocompleteEnabled = false; @Output() tableConfigChanged = new EventEmitter(); - @Output() investigationsFilterChanged = new EventEmitter(); - @Output() alertsFilterChanged = new EventEmitter(); + @Output() notificationsFilterChanged = new EventEmitter(); @Output() selected = new EventEmitter(); @ViewChild('titleTmp') titleTemplate: TemplateRef; @ViewChild('statusTmp') statusTemplate: TemplateRef; @@ -105,18 +103,10 @@ export class NotificationTabComponent implements AfterViewInit { this.notificationFilter = notificationFilter; const channel = notificationFilter['createdBy'] ? NotificationChannel.RECEIVER : NotificationChannel.SENDER; - if (this.notificationType === NotificationType.INVESTIGATION) { - this.investigationsFilterChanged.emit({ + this.notificationsFilterChanged.emit({ channel: channel, filter: notificationFilter, }); - } - if (this.notificationType === NotificationType.ALERT) { - this.alertsFilterChanged.emit({ - channel: channel, - filter: notificationFilter, - }); - } } public selectNotification(notification: Record): void { diff --git a/frontend/src/app/modules/shared/modules/notification/notification.module.ts b/frontend/src/app/modules/shared/modules/notification/notification.module.ts index 84f850e503..991f07f04c 100644 --- a/frontend/src/app/modules/shared/modules/notification/notification.module.ts +++ b/frontend/src/app/modules/shared/modules/notification/notification.module.ts @@ -23,27 +23,17 @@ import { CommonModule } from '@angular/common'; import { NgModule } from '@angular/core'; import { NotificationCommonModalComponent } from '@shared/components/notification-common-modal/notification-common-modal.component'; import { ModalModule } from '@shared/modules/modal/modal.module'; -import { AcceptNotificationModalComponent } from '@shared/modules/notification/modal/accept/accept-notification-modal.component'; -import { AcknowledgeNotificationModalComponent } from '@shared/modules/notification/modal/acknowledge/acknowledge-notification-modal.component'; -import { ApproveNotificationModalComponent } from '@shared/modules/notification/modal/approve/approve-notification-modal.component'; -import { CancelNotificationModalComponent } from '@shared/modules/notification/modal/cancel/cancel-notification-modal.component'; -import { DeclineNotificationModalComponent } from '@shared/modules/notification/modal/decline/decline-notification-modal.component'; +import { NotificationActionModalComponent } from '@shared/modules/notification/modal/actions/notification-action-modal.component'; import { NotificationTabComponent } from '@shared/modules/notification/notification-tab/notification-tab.component'; import { SharedModule } from '@shared/shared.module'; import { TemplateModule } from '@shared/template.module'; -import { CloseNotificationModalComponent } from './modal/close/close-notification-modal.component'; import { NotificationComponent } from './presentation/notification.component'; @NgModule({ declarations: [ NotificationComponent, NotificationTabComponent, - CloseNotificationModalComponent, - ApproveNotificationModalComponent, - CancelNotificationModalComponent, - AcceptNotificationModalComponent, - AcknowledgeNotificationModalComponent, - DeclineNotificationModalComponent, + NotificationActionModalComponent, NotificationCommonModalComponent, ], imports: [ CommonModule, TemplateModule, SharedModule, ModalModule ], @@ -51,12 +41,7 @@ import { NotificationComponent } from './presentation/notification.component'; NotificationCommonModalComponent, NotificationComponent, NotificationTabComponent, - CloseNotificationModalComponent, - ApproveNotificationModalComponent, - CancelNotificationModalComponent, - AcknowledgeNotificationModalComponent, - AcceptNotificationModalComponent, - DeclineNotificationModalComponent, + NotificationActionModalComponent, ], }) export class NotificationModule { diff --git a/frontend/src/app/modules/shared/modules/notification/presentation/notification.component.html b/frontend/src/app/modules/shared/modules/notification/presentation/notification.component.html index e713f1f297..81f7442ef1 100644 --- a/frontend/src/app/modules/shared/modules/notification/presentation/notification.component.html +++ b/frontend/src/app/modules/shared/modules/notification/presentation/notification.component.html @@ -35,19 +35,17 @@ @@ -60,11 +58,10 @@ diff --git a/frontend/src/app/modules/shared/modules/notification/presentation/notification.component.spec.ts b/frontend/src/app/modules/shared/modules/notification/presentation/notification.component.spec.ts index d81ba05e22..57d472b59a 100644 --- a/frontend/src/app/modules/shared/modules/notification/presentation/notification.component.spec.ts +++ b/frontend/src/app/modules/shared/modules/notification/presentation/notification.component.spec.ts @@ -83,21 +83,17 @@ describe('NotificationsInboxComponent', () => { }).pipe(delay(0)); const menuActionsConfig = []; const notificationType = NotificationType.INVESTIGATION; - const isInvestigation = true; return renderComponent( ``, { imports: [ SharedModule, NotificationModule, TemplateModule ], @@ -108,8 +104,7 @@ describe('NotificationsInboxComponent', () => { receivedNotifications$, clickHandler, menuActionsConfig, - notificationType, - isInvestigation, + notificationType }, }, ); diff --git a/frontend/src/app/modules/shared/modules/notification/presentation/notification.component.ts b/frontend/src/app/modules/shared/modules/notification/presentation/notification.component.ts index 4ea4446bc1..bf87c7a2fc 100644 --- a/frontend/src/app/modules/shared/modules/notification/presentation/notification.component.ts +++ b/frontend/src/app/modules/shared/modules/notification/presentation/notification.component.ts @@ -23,7 +23,7 @@ import { Component, EventEmitter, Input, Output } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; import { TableType } from '@shared/components/multi-select-autocomplete/table-type.model'; import { MenuActionConfig, TableEventConfig, TableHeaderSort } from '@shared/components/table/table.model'; -import { Notification, Notifications, NotificationType } from '@shared/model/notification.model'; +import { Notification, Notifications } from '@shared/model/notification.model'; import { View } from '@shared/model/view.model'; import { StaticIdService } from '@shared/service/staticId.service'; import { Observable } from 'rxjs'; @@ -44,13 +44,10 @@ export class NotificationComponent { @Input() queuedAndRequestedSortableColumns: Record = {}; @Input() receivedMultiSortList: TableHeaderSort[] = []; @Input() queuedAndRequestedMultiSortList: TableHeaderSort[] = []; - @Input() notificationType = NotificationType.INVESTIGATION; - @Input() isInvestigation: boolean = true; @Output() onReceivedTableConfigChanged = new EventEmitter(); @Output() onQueuedAndRequestedTableConfigChanged = new EventEmitter(); @Output() selected = new EventEmitter(); - @Output() investigationFilterChanged = new EventEmitter(); - @Output() alertFilterChanged = new EventEmitter(); + @Output() notificationsFilterChanged = new EventEmitter(); public readonly tabIndex$ = this.route.queryParams.pipe(map(params => parseInt(params.tabIndex, 10) || 0)); diff --git a/frontend/src/app/modules/shared/service/notification.service.spec.ts b/frontend/src/app/modules/shared/service/notification.service.spec.ts index 761f64441e..35aa48109e 100644 --- a/frontend/src/app/modules/shared/service/notification.service.spec.ts +++ b/frontend/src/app/modules/shared/service/notification.service.spec.ts @@ -16,102 +16,97 @@ * * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ -import {NotificationService} from "@shared/service/notification.service"; -import {HttpClientTestingModule, HttpTestingController} from "@angular/common/http/testing"; -import {TestBed} from "@angular/core/testing"; -import {NotificationStatus} from "@shared/model/notification.model"; -import {NotificationChannel} from "@shared/components/multi-select-autocomplete/table-type.model"; -import {AuthService} from "@core/auth/auth.service"; -import {ApiService} from "@core/api/api.service"; -import {KeycloakService} from "keycloak-angular"; +import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; +import { TestBed } from '@angular/core/testing'; +import { ApiService } from '@core/api/api.service'; +import { AuthService } from '@core/auth/auth.service'; +import { NotificationChannel } from '@shared/components/multi-select-autocomplete/table-type.model'; +import { NotificationStatus } from '@shared/model/notification.model'; +import { NotificationService } from '@shared/service/notification.service'; +import { KeycloakService } from 'keycloak-angular'; describe('NotificationService', () => { - let service: NotificationService; - let httpTestingController: HttpTestingController; - let authService: AuthService; - - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [HttpClientTestingModule], - providers: [NotificationService, ApiService, KeycloakService, AuthService], - }); - service = TestBed.inject(NotificationService); - httpTestingController = TestBed.inject(HttpTestingController); - authService = TestBed.inject(AuthService); + let service: NotificationService; + let httpTestingController: HttpTestingController; + let authService: AuthService; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [ HttpClientTestingModule ], + providers: [ NotificationService, ApiService, KeycloakService, AuthService ], }); + service = TestBed.inject(NotificationService); + httpTestingController = TestBed.inject(HttpTestingController); + authService = TestBed.inject(AuthService); + }); - afterEach(() => { - httpTestingController.verify(); - }); + afterEach(() => { + httpTestingController.verify(); + }); - it('should close a notification', () => { - const notificationId = '123'; - const reason = 'Test reason'; - const isInvestigation = true; - spyOn(authService, 'getBearerToken').and.returnValue('testtoken'); + it('should close a notification', () => { + const notificationId = '123'; + const reason = 'Test reason'; + spyOn(authService, 'getBearerToken').and.returnValue('testtoken'); - service.closeNotification(notificationId, reason, isInvestigation).subscribe(); + service.closeNotification(notificationId, reason).subscribe(); - const req = httpTestingController.expectOne(`${service.notificationUrl()}/${notificationId}/close`); - expect(req.request.method).toBe('POST'); - expect(req.request.body).toEqual( '{"reason":"Test reason"}'); - req.flush({}); - }); + const req = httpTestingController.expectOne(`${ service.notificationUrl() }/${ notificationId }/close`); + expect(req.request.method).toBe('POST'); + expect(req.request.body).toEqual('{"reason":"Test reason"}'); + req.flush({}); + }); - it('should approve a notification', () => { - const notificationId = '123'; - const isInvestigation = true; - spyOn(authService, 'getBearerToken').and.returnValue('testtoken'); + it('should approve a notification', () => { + const notificationId = '123'; + spyOn(authService, 'getBearerToken').and.returnValue('testtoken'); - service.approveNotification(notificationId, isInvestigation).subscribe(); + service.approveNotification(notificationId).subscribe(); - const req = httpTestingController.expectOne(`${service.notificationUrl()}/${notificationId}/approve`); - expect(req.request.method).toBe('POST'); - req.flush({}); - }); + const req = httpTestingController.expectOne(`${ service.notificationUrl() }/${ notificationId }/approve`); + expect(req.request.method).toBe('POST'); + req.flush({}); + }); - it('should cancel a notification', () => { - const notificationId = '123'; - const isInvestigation = true; - spyOn(authService, 'getBearerToken').and.returnValue('testtoken'); + it('should cancel a notification', () => { + const notificationId = '123'; + spyOn(authService, 'getBearerToken').and.returnValue('testtoken'); - service.cancelNotification(notificationId, isInvestigation).subscribe(); + service.cancelNotification(notificationId).subscribe(); - const req = httpTestingController.expectOne(`${service.notificationUrl()}/${notificationId}/cancel`); - expect(req.request.method).toBe('POST'); - req.flush({}); - }); + const req = httpTestingController.expectOne(`${ service.notificationUrl() }/${ notificationId }/cancel`); + expect(req.request.method).toBe('POST'); + req.flush({}); + }); - it('should update a notification', () => { - const notificationId = '123'; - const status = NotificationStatus.ACKNOWLEDGED; - const reason = 'Test reason'; - const isInvestigation = true; - spyOn(authService, 'getBearerToken').and.returnValue('testtoken'); + it('should update a notification', () => { + const notificationId = '123'; + const status = NotificationStatus.ACKNOWLEDGED; + const reason = 'Test reason'; + spyOn(authService, 'getBearerToken').and.returnValue('testtoken'); - service.updateNotification(notificationId, status, reason, isInvestigation).subscribe(); + service.updateNotification(notificationId, status, reason).subscribe(); - const req = httpTestingController.expectOne(`${service.notificationUrl()}/${notificationId}/update`); - expect(req.request.method).toBe('POST'); - expect(req.request.body).toEqual('{"reason":"Test reason","status":"ACKNOWLEDGED"}'); - req.flush({}); - }); + const req = httpTestingController.expectOne(`${ service.notificationUrl() }/${ notificationId }/update`); + expect(req.request.method).toBe('POST'); + expect(req.request.body).toEqual('{"reason":"Test reason","status":"ACKNOWLEDGED"}'); + req.flush({}); + }); - it('should get distinct filter values', () => { - const channel: NotificationChannel = NotificationChannel.SENDER; - const fieldNames = 'SomeField'; - const startsWith = 'Test'; - const isInvestigation = true; - spyOn(authService, 'getBearerToken').and.returnValue('testtoken'); + it('should get distinct filter values', () => { + const channel: NotificationChannel = NotificationChannel.SENDER; + const fieldNames = 'SomeField'; + const startsWith = 'Test'; + spyOn(authService, 'getBearerToken').and.returnValue('testtoken'); - service.getDistinctFilterValues(channel, fieldNames, startsWith, isInvestigation).subscribe(); + service.getDistinctFilterValues(channel, fieldNames, startsWith).subscribe(); - const req = httpTestingController.expectOne( - `${service.notificationUrl()}/distinctFilterValues?fieldName=SomeField&startWith=Test&size=200&channel=SENDER` - ); - expect(req.request.method).toBe('GET'); - req.flush({}); - }); + const req = httpTestingController.expectOne( + `${ service.notificationUrl() }/distinctFilterValues?fieldName=SomeField&startWith=Test&size=200&channel=SENDER`, + ); + expect(req.request.method).toBe('GET'); + req.flush({}); + }); }); diff --git a/frontend/src/app/modules/shared/service/notification.service.ts b/frontend/src/app/modules/shared/service/notification.service.ts index 6c76a13b60..bc3f312a57 100644 --- a/frontend/src/app/modules/shared/service/notification.service.ts +++ b/frontend/src/app/modules/shared/service/notification.service.ts @@ -48,33 +48,11 @@ export class NotificationService { constructor(private readonly apiService: ApiService) { } - - // TODO: merge functions for created and received notifications - public getCreated(page: number, pageSize: number, sorting: TableHeaderSort[], filter?: NotificationDeeplinkFilter, fullFilter?: any, isInvestigation = true): Observable { - const sort = sorting.length ? sorting.map(array => `${ array[0] },${ array[1] }`) : [ 'createdDate,desc' ]; - const requestUrl = this.notificationUrl() + '/filter'; - const additionalFilters = new Set([ ...provideFilterListForNotifications(filter, fullFilter), 'channel,EQUAL,SENDER,AND' ]); - - const body = { - pageAble: { - page: page, - size: pageSize, - sort: [ ...sort ], - }, - searchCriteria: { - filter: [ ...additionalFilters ], - }, - }; - - return this.apiService - .post(requestUrl, body) - .pipe(map(data => NotificationAssembler.assembleNotifications(data))); - } - - public getReceived(page: number, pageSize: number, sorting: TableHeaderSort[], filter?: NotificationDeeplinkFilter, fullFilter?: any, isInvestigation = true): Observable { + public getNotifications(page: number, pageSize: number, sorting: TableHeaderSort[], channel: NotificationChannel, filter?: NotificationDeeplinkFilter, fullFilter?: any): Observable { const sort = sorting.length ? sorting.map(array => `${ array[0] },${ array[1] }`) : [ 'createdDate,desc' ]; const requestUrl = this.notificationUrl() + '/filter'; - const additionalFilters = new Set([ ...provideFilterListForNotifications(filter, fullFilter), 'channel,EQUAL,RECEIVER,AND' ]); + const channelFilter = channel === NotificationChannel.RECEIVER ? 'channel,EQUAL,RECEIVER,AND' : 'channel,EQUAL,SENDER,AND'; + const additionalFilters = new Set([ ...provideFilterListForNotifications(filter, fullFilter), channelFilter ]); const body = { pageAble: { @@ -93,32 +71,32 @@ export class NotificationService { } - public getNotificationById(id: string, isInvestigation = true): Observable { + public getNotificationById(id: string): Observable { const requestUrl = this.notificationUrl(); return this.apiService .get(`${ requestUrl }/${ id }`) .pipe(map(notification => NotificationAssembler.assembleNotification(notification))); } - public createNotification(partIds: string[], description: string, severity: Severity, bpn: string, isAsBuilt: boolean, type: string): Observable { - const body = { partIds, description, severity, receiverBpn: bpn, isAsBuilt, type }; + public createNotification(partIds: string[], description: string, severity: Severity, bpn: string, isAsBuilt: boolean, type: string, title: string): Observable { + const body = { partIds, description, severity, receiverBpn: bpn, isAsBuilt, type, title }; return this.apiService.post(`${ this.url }/notifications`, body).pipe(map(({ id }) => id)); } - public closeNotification(id: string, reason: string, isInvestigation = true): Observable { + public closeNotification(id: string, reason: string): Observable { const requestUrl = this.notificationUrl(); const body = { reason }; return this.apiService.post(`${ requestUrl }/${ id }/close`, body); } - public approveNotification(id: string, isInvestigation = true): Observable { + public approveNotification(id: string): Observable { const requestUrl = this.notificationUrl(); return this.apiService.post(`${ requestUrl }/${ id }/approve`); } - public cancelNotification(id: string, isInvestigation = true): Observable { + public cancelNotification(id: string): Observable { const requestUrl = this.notificationUrl(); return this.apiService.post(`${ requestUrl }/${ id }/cancel`); } @@ -126,14 +104,14 @@ export class NotificationService { public updateNotification( id: string, status: NotificationStatus.ACKNOWLEDGED | NotificationStatus.ACCEPTED | NotificationStatus.DECLINED, - reason = '', isInvestigation = true, + reason = '', ): Observable { const requestUrl = this.notificationUrl(); const body = { reason, status }; return this.apiService.post(`${ requestUrl }/${ id }/update`, body); } - public getDistinctFilterValues(channel: NotificationChannel, fieldNames: string, startsWith: string, isInvestigation = true) { + public getDistinctFilterValues(channel: NotificationChannel, fieldNames: string, startsWith: string) { const mappedFieldName = PartsAssembler.mapFieldNameToApi(fieldNames); const requestUrl = this.notificationUrl(); let params = new HttpParams() diff --git a/frontend/src/assets/locales/de/common.json b/frontend/src/assets/locales/de/common.json index c717fa93e3..9f9eb9088d 100644 --- a/frontend/src/assets/locales/de/common.json +++ b/frontend/src/assets/locales/de/common.json @@ -8,7 +8,7 @@ "about": "Über uns", "relations": "Beziehungen", "admin": "Verwaltung", - "alerts": "Inbox", + "inbox": "Inbox", "adminRegistry": "Registry-Abfragen", "adminBpn": "BPN - EDC Konfiguration", "adminImport": "Datenbereitstellung", @@ -226,6 +226,48 @@ "emptyRange": "0 von {{length}}", "range": "{{startIndex}} – {{endIndex}} von {{length}}" }, + "commonInvestigation": { + "viewAll": "Alle anzeigen", + "tabs": { + "received": "Zugewiesene", + "queuedAndRequested": "Erstellte und Angefragte" + }, + "status": { + "RECEIVED": "Erhalten", + "CREATED": "Erstellt", + "SENT": "Zugestellt", + "CANCELED": "Abgebrochen", + "CLOSED": "Abgeschlossen", + "ACCEPTED": "Akzeptiert", + "ACKNOWLEDGED": "Bestätigt", + "DECLINED": "Abgelehnt" + }, + "modal": { + "acceptTitle": "Annehmen der Untersuchung", + "acceptReasonHint": "Geben Sie den Grund für die Annahme dieser Untersuchung ein.", + "acknowledgeTitle": "Bestätigung der Untersuchung", + "approvalTitle": "Genehmigung der Untersuchung", + "closeTitle": "Schließen der Untersuchung", + "closeReasonHint": "Geben Sie den Grund für das Schließen ein.", + "cancellationTitle": "Abbruch der Untersuchung", + "cancellationHint": "Geben Sie die ID der Untersuchung ein, um Ihre Abbruchanfrage zu bestätigen.", + "cancellationConfirmationLabel": "Ja, ich bestätige die Stornierung der Untersuchung mit der ID: ", + "declineTitle": "Ablehnung der Untersuchung", + "declineReasonHint": "Geben Sie den Grund für die Ablehnung dieser Untersuchung ein.", + "successfullyAccepted": "Untersuchung wurde erfolgreich angenommen.", + "successfullyAcknowledged": "Untersuchung wurde erfolgreich bestätigt", + "successfullyApproved": "Untersuchung wurde erfolgreich genehmigt.", + "successfullyCanceled": "Untersuchung wurde erfolgreich abgebrochen.", + "successfullyClosed": "Untersuchung wurde erfolgreich geschlossen.", + "successfullyDeclined": "Untersuchung wurde erfolgreich abgelehnt.", + "failedAccept": "Untersuchung konnte nicht akzeptiert werden, bitte versuchen Sie es erneut.", + "failedAcknowledge": "Untersuchung konnte nicht bestätigt werden, bitte versuchen Sie es erneut.", + "failedApprove": "Die Untersuchung konnte nicht genehmigt werden, bitte versuchen Sie es erneut.", + "failedCancel": "Die Untersuchung konnte nicht gelöscht werden, bitte versuchen Sie es erneut.", + "failedClose": "Die Untersuchung konnte nicht abgeschlossen werden, bitte versuchen Sie es erneut.", + "failedDecline": "Untersuchung konnte nicht abgelehnt werden, bitte versuchen Sie es erneut." + } + }, "commonAlert": { "viewAll": "Alle anzeigen", "tabs": { diff --git a/frontend/src/assets/locales/en/common.json b/frontend/src/assets/locales/en/common.json index aa869d6be9..c94cdaf946 100644 --- a/frontend/src/assets/locales/en/common.json +++ b/frontend/src/assets/locales/en/common.json @@ -8,7 +8,7 @@ "about": "About", "relations": "Relations", "admin": "Administration", - "alerts": "Inbox", + "inbox": "Inbox", "adminRegistry": "Registry lookups", "adminBpn": "BPN - EDC configuration", "adminImport": "Data provisioning", @@ -227,6 +227,48 @@ "emptyRange": "0 of {{length}}", "range": "{{startIndex}} – {{endIndex}} of {{length}}" }, + "commonInvestigation": { + "viewAll": "View all", + "tabs": { + "received": "Received", + "queuedAndRequested": "Queued & Requested" + }, + "status": { + "SENT": "Requested", + "CANCELED": "Cancelled", + "CLOSED": "Closed", + "CREATED": "Queued", + "RECEIVED": "Received", + "ACCEPTED": "Accepted", + "ACKNOWLEDGED": "Acknowledged", + "DECLINED": "Declined" + }, + "modal": { + "acceptTitle": "Accept of investigation", + "acceptReasonHint": "Enter the reason for accepting this investigation.", + "acknowledgeTitle": "Acknowledgment of investigation", + "approvalTitle": "Approval of investigation", + "closeTitle": "Close of investigation", + "closeReasonHint": "Enter the reason for close action.", + "cancellationTitle": "Cancellation of investigation", + "cancellationHint": "Enter the ID of the investigation to confirm your cancellation.", + "cancellationConfirmationLabel": "Yes, I confirm that I want to cancel the investigation with the ID: ", + "declineTitle": "Decline of investigation", + "declineReasonHint": "Enter the reason for declining this investigation.", + "successfullyAccepted": "Investigation was accepted successfully.", + "successfullyAcknowledged": "Investigation was acknowledged successfully.", + "successfullyApproved": "Investigation was approved successfully.", + "successfullyCanceled": "Investigation was canceled successfully.", + "successfullyClosed": "Investigation was closed successfully.", + "successfullyDeclined": "Investigation was declined successfully.", + "failedAccept": "Investigation failed to accept, please try again.", + "failedAcknowledge": "Investigation failed to acknowledge, please try again.", + "failedApprove": "Investigation failed to approve, please try again.", + "failedCancel": "Investigation failed to cancel, please try again.", + "failedClose": "Investigation failed to close, please try again.", + "failedDecline": "Investigation failed to decline, please try again." + } + }, "commonAlert": { "viewAll": "View all", "tabs": { diff --git a/tx-backend/src/main/java/org/eclipse/tractusx/traceability/common/model/SecurityUtils.java b/tx-backend/src/main/java/org/eclipse/tractusx/traceability/common/model/SecurityUtils.java index df8603ec3e..e739309b34 100644 --- a/tx-backend/src/main/java/org/eclipse/tractusx/traceability/common/model/SecurityUtils.java +++ b/tx-backend/src/main/java/org/eclipse/tractusx/traceability/common/model/SecurityUtils.java @@ -57,6 +57,7 @@ public static StartNotificationRequest sanitize(StartNotificationRequest request String cleanReceiverBpn = sanitize(request.getReceiverBpn()); List cleanPartIds = sanitize(request.getPartIds()); return StartNotificationRequest.builder() + .title(request.getTitle()) .description(cleanDescription) .targetDate(request.getTargetDate()) .severity(request.getSeverity()) diff --git a/tx-backend/src/main/java/org/eclipse/tractusx/traceability/notification/application/notification/mapper/NotificationResponseMapper.java b/tx-backend/src/main/java/org/eclipse/tractusx/traceability/notification/application/notification/mapper/NotificationResponseMapper.java index 78fb1bfb73..f7adc8e55b 100644 --- a/tx-backend/src/main/java/org/eclipse/tractusx/traceability/notification/application/notification/mapper/NotificationResponseMapper.java +++ b/tx-backend/src/main/java/org/eclipse/tractusx/traceability/notification/application/notification/mapper/NotificationResponseMapper.java @@ -53,6 +53,7 @@ public static NotificationResponse from(Notification notification) { .assetIds(Collections.unmodifiableList(notification.getAssetIds())) .channel(NotificationMessageMapper.from(notification.getNotificationSide())) .type(NotificationMessageMapper.from(notification.getNotificationType())) + .title(notification.getTitle()) .reason(new NotificationReasonResponse( notification.getCloseReason(), notification.getAcceptReason(), diff --git a/tx-backend/src/main/java/org/eclipse/tractusx/traceability/notification/domain/base/model/NotificationStatus.java b/tx-backend/src/main/java/org/eclipse/tractusx/traceability/notification/domain/base/model/NotificationStatus.java index c05a9ceb44..dcab76eac1 100644 --- a/tx-backend/src/main/java/org/eclipse/tractusx/traceability/notification/domain/base/model/NotificationStatus.java +++ b/tx-backend/src/main/java/org/eclipse/tractusx/traceability/notification/domain/base/model/NotificationStatus.java @@ -80,11 +80,12 @@ public static NotificationStatus fromStringValue(String value) { return MAPPINGS.get(value); } - public static NotificationStatus getPreviousStatus(NotificationStatus status) { + public static NotificationStatus getPreviousStatus(NotificationStatus status, List messages) { return switch (status) { case CREATED, SENT, CANCELED -> NotificationStatus.CREATED; - case ACKNOWLEDGED, RECEIVED, CLOSED -> NotificationStatus.SENT; + case ACKNOWLEDGED, RECEIVED -> NotificationStatus.SENT; case ACCEPTED, DECLINED -> NotificationStatus.ACKNOWLEDGED; + case CLOSED -> messages.size() > 1 ? NotificationStatus.SENT : NotificationStatus.CREATED; }; } public boolean transitionAllowed(NotificationStatus to) { diff --git a/tx-backend/src/main/java/org/eclipse/tractusx/traceability/notification/domain/base/service/AbstractNotificationService.java b/tx-backend/src/main/java/org/eclipse/tractusx/traceability/notification/domain/base/service/AbstractNotificationService.java index e81db3cab7..c09dad7b83 100644 --- a/tx-backend/src/main/java/org/eclipse/tractusx/traceability/notification/domain/base/service/AbstractNotificationService.java +++ b/tx-backend/src/main/java/org/eclipse/tractusx/traceability/notification/domain/base/service/AbstractNotificationService.java @@ -71,12 +71,13 @@ public NotificationId start(StartNotification startNotification) { public void update(Long notificationId, NotificationStatus notificationStatus, String reason) { Notification notification = loadOrNotFoundException(new NotificationId(notificationId)); - NotificationStatus previousStatus = NotificationStatus.getPreviousStatus(notificationStatus); + List messages = notification.getNotifications(); + NotificationStatus previousStatus = NotificationStatus.getPreviousStatus(notificationStatus, messages); /* Create a copy of the latest notifications. As per asset there will be a notification created on start it is possible that several elements with the same previous state are returned.*/ - notification.getNotifications().stream() + messages.stream() .filter(notificationMessage -> notificationMessage.getNotificationStatus().equals(previousStatus)) .forEach(notificationMessage -> { NotificationMessage notificationMessageSwitchedSenderAndReceiver = notificationMessage.copyAndSwitchSenderAndReceiver(traceabilityProperties.getBpn()); diff --git a/tx-models/src/main/java/notification/request/StartNotificationRequest.java b/tx-models/src/main/java/notification/request/StartNotificationRequest.java index 2bf7190f94..3aa31ebb11 100644 --- a/tx-models/src/main/java/notification/request/StartNotificationRequest.java +++ b/tx-models/src/main/java/notification/request/StartNotificationRequest.java @@ -39,7 +39,7 @@ @AllArgsConstructor public class StartNotificationRequest { - @Size(min = 1, max = 255, message = "Specify at least 1 and at most 50 assetIds") + @Size(min = 1, max = 255, message = "Specify at least 1 and at most 255 characters for the title") @Schema(example = "title", minLength = 1, maxLength = 255) private String title;