From 2b21399d89f648a72940daee603ef0bc537e88a7 Mon Sep 17 00:00:00 2001 From: Martin Maul Date: Fri, 5 Jul 2024 14:37:38 +0200 Subject: [PATCH 1/5] chore(notifications): 999 notification processing handler --- .../policies/policies.component.ts | 2 +- .../presentation/notifications.component.ts | 3 +++ .../notificationMenuActions.assembler.ts | 19 +++++++++----- .../components/table/table.component.html | 6 ++--- .../components/table/table.component.ts | 15 +++++++++-- .../shared/components/table/table.model.ts | 2 +- .../notification-action-modal.component.ts | 10 +++++-- .../notification-tab.component.html | 23 +++++++++++++--- .../notification-tab.component.ts | 4 +++ .../notification-processing.service.ts | 26 +++++++++++++++++++ 10 files changed, 89 insertions(+), 21 deletions(-) create mode 100644 frontend/src/app/modules/shared/service/notification-processing.service.ts diff --git a/frontend/src/app/modules/page/admin/presentation/policy-management/policies/policies.component.ts b/frontend/src/app/modules/page/admin/presentation/policy-management/policies/policies.component.ts index 9bb3de39c3..5a30375056 100644 --- a/frontend/src/app/modules/page/admin/presentation/policy-management/policies/policies.component.ts +++ b/frontend/src/app/modules/page/admin/presentation/policy-management/policies/policies.component.ts @@ -55,7 +55,7 @@ export class PoliciesComponent { label: 'actions.edit', icon: 'edit', action: (selectedPolicy: Record) => this.openEditView(selectedPolicy), - isAuthorized: this.roleService.isAdmin(), + isAuthorized: data => this.roleService.isAdmin(), } ], sortableColumns: { select: false, 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 252a610543..38288aa7a4 100644 --- a/frontend/src/app/modules/page/notifications/presentation/notifications.component.ts +++ b/frontend/src/app/modules/page/notifications/presentation/notifications.component.ts @@ -39,6 +39,7 @@ import { NotificationType, } from '@shared/model/notification.model'; import { TranslationContext } from '@shared/model/translation-context.model'; +import { NotificationProcessingService } from '@shared/service/notification-processing.service'; import { Subscription } from 'rxjs'; @Component({ @@ -74,6 +75,7 @@ export class NotificationsComponent { private readonly route: ActivatedRoute, private readonly cd: ChangeDetectorRef, private readonly toastService: ToastService, + private readonly notificationProcessingService: NotificationProcessingService, ) { this.notificationsReceived$ = this.notificationsFacade.notificationsReceived$; this.notificationsQueuedAndRequested$ = this.notificationsFacade.notificationsQueuedAndRequested$; @@ -114,6 +116,7 @@ export class NotificationsComponent { this.menuActionsConfig = NotificationMenuActionsAssembler.getMenuActions( this.actionHelperService, this.notificationCommonModalComponent, + this.notificationProcessingService, ); this.cd.detectChanges(); } diff --git a/frontend/src/app/modules/shared/assembler/notificationMenuActions.assembler.ts b/frontend/src/app/modules/shared/assembler/notificationMenuActions.assembler.ts index 036039f8c3..48d4959a22 100644 --- a/frontend/src/app/modules/shared/assembler/notificationMenuActions.assembler.ts +++ b/frontend/src/app/modules/shared/assembler/notificationMenuActions.assembler.ts @@ -21,51 +21,56 @@ import { NotificationCommonModalComponent } from '@shared/components/notificatio import { MenuActionConfig } from '@shared/components/table/table.model'; import { Notification, NotificationStatus } from '@shared/model/notification.model'; import { NotificationAction } from '@shared/modules/notification/notification-action.enum'; +import { NotificationProcessingService } from '@shared/service/notification-processing.service'; export class NotificationMenuActionsAssembler { - public static getMenuActions(helperService: NotificationActionHelperService, modal: NotificationCommonModalComponent): MenuActionConfig[] { + public static getMenuActions(helperService: NotificationActionHelperService, modal: NotificationCommonModalComponent, notificationProcessingService: NotificationProcessingService): MenuActionConfig[] { + const notInLoadingState = (id: string) => { + console.log(id); + return !notificationProcessingService.notificationIdsInLoadingState.has(id); + }; return [ { label: 'actions.close', icon: 'close', action: data => modal.show(NotificationStatus.CLOSED, data), condition: data => helperService.showCloseButton(data), - isAuthorized: helperService.isAuthorizedForButton(NotificationAction.CLOSE), + isAuthorized: data => helperService.isAuthorizedForButton(NotificationAction.CLOSE) && !notificationProcessingService.notificationIdsInLoadingState.has(data?.id), }, { label: 'actions.approve', icon: 'share', action: data => modal.show(NotificationStatus.APPROVED, data), condition: data => helperService.showApproveButton(data), - isAuthorized: helperService.isAuthorizedForButton(NotificationAction.APPROVE), + isAuthorized: data => helperService.isAuthorizedForButton(NotificationAction.APPROVE) && !notificationProcessingService.notificationIdsInLoadingState.has(data?.id), }, { label: 'actions.cancel', icon: 'cancel', action: data => modal.show(NotificationStatus.CANCELED, data), condition: data => helperService.showCancelButton(data), - isAuthorized: helperService.isAuthorizedForButton(NotificationAction.CANCEL), + isAuthorized: data => helperService.isAuthorizedForButton(NotificationAction.CANCEL) && !notificationProcessingService.notificationIdsInLoadingState.has(data?.id), }, { label: 'actions.acknowledge', icon: 'work', action: data => modal.show(NotificationStatus.ACKNOWLEDGED, data), condition: data => helperService.showAcknowledgeButton(data), - isAuthorized: helperService.isAuthorizedForButton(NotificationAction.ACKNOWLEDGE), + isAuthorized: data => helperService.isAuthorizedForButton(NotificationAction.ACKNOWLEDGE) && !notificationProcessingService.notificationIdsInLoadingState.has(data?.id), }, { label: 'actions.accept', icon: 'assignment_turned_in', action: data => modal.show(NotificationStatus.ACCEPTED, data), condition: data => helperService.showAcceptButton(data), - isAuthorized: helperService.isAuthorizedForButton(NotificationAction.ACCEPT), + isAuthorized: data => helperService.isAuthorizedForButton(NotificationAction.ACCEPT) && !notificationProcessingService.notificationIdsInLoadingState.has(data?.id), }, { label: 'actions.decline', icon: 'assignment_late', action: data => modal.show(NotificationStatus.DECLINED, data), condition: data => helperService.showDeclineButton(data), - isAuthorized: helperService.isAuthorizedForButton(NotificationAction.DECLINE), + isAuthorized: data => helperService.isAuthorizedForButton(NotificationAction.DECLINE) && !notificationProcessingService.notificationIdsInLoadingState.has(data?.id), }, ]; } diff --git a/frontend/src/app/modules/shared/components/table/table.component.html b/frontend/src/app/modules/shared/components/table/table.component.html index 2e60fae8e0..cc6c96ab7b 100644 --- a/frontend/src/app/modules/shared/components/table/table.component.html +++ b/frontend/src/app/modules/shared/components/table/table.component.html @@ -338,12 +338,10 @@

{{ 'table.noResultFound' | i18n }}

matTooltipPosition="above" [class.mdc-tooltip--multiline]="true" [matTooltipShowDelay]="1000" - [matTooltipDisabled]="config.isAuthorized!==false" *ngIf="!config.condition || config.condition(row)" class="menu-action-button" - [class.menu-action-button--unauthorized]="(config.isAuthorized === false) ? 'unauthorized' : ''" - (click)="config.isAuthorized === false ? null : config.action(row)" - onkeydown="config.isAuthorized === false ? null : config.action(row)" + [class.menu-action-button--unauthorized]="(config?.isAuthorized(row) === false) ? 'unauthorized' : ''" + [attr.data-testId]="'table-menu-button--' + config.label" mat-menu-item > diff --git a/frontend/src/app/modules/shared/components/table/table.component.ts b/frontend/src/app/modules/shared/components/table/table.component.ts index da4308286b..761d9bccd4 100644 --- a/frontend/src/app/modules/shared/components/table/table.component.ts +++ b/frontend/src/app/modules/shared/components/table/table.component.ts @@ -20,7 +20,16 @@ ********************************************************************************/ import { SelectionModel } from '@angular/cdk/collections'; -import { Component, ElementRef, EventEmitter, Input, Output, ViewChild, ViewEncapsulation } from '@angular/core'; +import { + Component, + ElementRef, + EventEmitter, + Input, + Optional, + Output, + ViewChild, + ViewEncapsulation, +} from '@angular/core'; import { FormGroup } from '@angular/forms'; import { MatDialog, MatDialogConfig } from '@angular/material/dialog'; import { MatPaginator, PageEvent } from '@angular/material/paginator'; @@ -48,6 +57,7 @@ import { isDateFilter } from '@shared/helper/filter-helper'; import { addSelectedValues, clearAllRows, clearCurrentRows, removeSelectedValues } from '@shared/helper/table-helper'; import { NotificationStatus } from '@shared/model/notification.model'; import { FlattenObjectPipe } from '@shared/pipes/flatten-object.pipe'; +import { NotificationProcessingService } from '@shared/service/notification-processing.service'; @Component({ selector: 'app-table', @@ -187,6 +197,7 @@ export class TableComponent { private tableSettingsService: TableSettingsService, public toastService: ToastService, private readonly router: Router, + @Optional() public readonly notificationProcessingService: NotificationProcessingService, ) { } @@ -401,7 +412,7 @@ export class TableComponent { icon: 'edit', action: (data: Record) => this.editClicked.emit(data), condition: data => this.isEditable(data), - isAuthorized: this.roleService.isSupervisor() || this.roleService.isUser(), + isAuthorized: data => this.roleService.isSupervisor() || this.roleService.isUser(), }; const defaultActionsToAdd: MenuActionConfig[] = [ viewDetailsMenuAction, editDetailsMenuAction ] .filter(action => !menuActionsConfig.some(a => a.label === action.label)); diff --git a/frontend/src/app/modules/shared/components/table/table.model.ts b/frontend/src/app/modules/shared/components/table/table.model.ts index 36d1febc34..348ce8c91e 100644 --- a/frontend/src/app/modules/shared/components/table/table.model.ts +++ b/frontend/src/app/modules/shared/components/table/table.model.ts @@ -55,5 +55,5 @@ export interface MenuActionConfig { icon: string; action: (data: T) => void; condition?: (data: T) => boolean; - isAuthorized?: boolean; + isAuthorized?: (data: T) => boolean; } 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 index bbc57937fd..4f0e872943 100644 --- 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 @@ -26,6 +26,7 @@ import { getTranslationContext } from '@shared/helper/notification-helper'; import { Notification, NotificationStatus } from '@shared/model/notification.model'; import { ModalData } from '@shared/modules/modal/core/modal.model'; import { ModalService } from '@shared/modules/modal/core/modal.service'; +import { NotificationProcessingService } from '@shared/service/notification-processing.service'; import { Observable } from 'rxjs'; @Component({ @@ -44,7 +45,7 @@ export class NotificationActionModalComponent { private readonly textAreaControl = new UntypedFormControl(); public reasonHintLabel = null; - constructor(private readonly toastService: ToastService, private readonly confirmModalService: ModalService) { + constructor(private readonly toastService: ToastService, private readonly confirmModalService: ModalService, private readonly notificationProcessingService: NotificationProcessingService) { this.formGroup = new UntypedFormGroup({ reason: this.textAreaControl }); } @@ -123,14 +124,19 @@ export class NotificationActionModalComponent { } const onConfirm = (isConfirmed: boolean) => { if (!isConfirmed) return; - + this.notificationProcessingService.notificationIdsInLoadingState.add(notification.id); + console.log('START ', this.notificationProcessingService.notificationIdsInLoadingState); const reason = this.formGroup.get('reason').value; this.callback(desiredStatus, notification.id, reason).subscribe({ next: () => { + this.notificationProcessingService.notificationIdsInLoadingState.delete(notification.id); + console.log('STOP WITH SUCCESS: ', this.notificationProcessingService.notificationIdsInLoadingState); this.toastService.success(modalData.successMessage); this.confirmActionCompleted.emit(); }, error: () => { + this.notificationProcessingService.notificationIdsInLoadingState.delete(notification.id); + console.log('STOP WITH ERROR: ', this.notificationProcessingService.notificationIdsInLoadingState); this.toastService.error(modalData.errorMessage, 15000, true); this.confirmActionCompleted.emit(); }, 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 7d80387836..4f4e60fc70 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 @@ -52,14 +52,29 @@ + +
+ +
+
+

{{ title }}

- -
- {{ translationContext + '.status.' + status | i18n }} -
+ + +
+ {{ translationContext + '.status.' + status | i18n }} +
+
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 a48fcbbe8b..90d8c2394c 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 @@ -31,6 +31,7 @@ import { } from '@shared/components/table/table.model'; import { Notification, NotificationFilter, Notifications, NotificationType } from '@shared/model/notification.model'; import { View } from '@shared/model/view.model'; +import { NotificationProcessingService } from '@shared/service/notification-processing.service'; import { Observable } from 'rxjs'; @Component({ @@ -71,6 +72,9 @@ export class NotificationTabComponent implements AfterViewInit { notificationFilter: NotificationFilter; + constructor(public readonly notificationProcessingService: NotificationProcessingService) { + } + public ngAfterViewInit(): void { const defaultColumns: DisplayColumns[] = [ 'description', 'title', 'status', 'createdDate' ]; diff --git a/frontend/src/app/modules/shared/service/notification-processing.service.ts b/frontend/src/app/modules/shared/service/notification-processing.service.ts new file mode 100644 index 0000000000..eb05947ea8 --- /dev/null +++ b/frontend/src/app/modules/shared/service/notification-processing.service.ts @@ -0,0 +1,26 @@ +import { Injectable } from '@angular/core'; + +/******************************************************************************** + * Copyright (c) 2024 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 + ********************************************************************************/ +@Injectable({ + providedIn: 'root', +}) +export class NotificationProcessingService { + notificationIdsInLoadingState: Set = new Set(); +} From e9e94c613a7f209c9840e9855eae15cd2d3914ee Mon Sep 17 00:00:00 2001 From: Martin Maul Date: Wed, 10 Jul 2024 10:26:17 +0200 Subject: [PATCH 2/5] chore(notifications): 999 notification processing handler --- .../policies/policies.component.ts | 2 +- .../notificationMenuActions.assembler.ts | 19 +++++++++++++------ .../components/table/table.component.html | 6 ++++-- .../components/table/table.component.ts | 6 ++++-- .../shared/components/table/table.model.ts | 3 ++- .../notification-processing.service.ts | 5 +++++ 6 files changed, 29 insertions(+), 12 deletions(-) diff --git a/frontend/src/app/modules/page/admin/presentation/policy-management/policies/policies.component.ts b/frontend/src/app/modules/page/admin/presentation/policy-management/policies/policies.component.ts index 5a30375056..9bb3de39c3 100644 --- a/frontend/src/app/modules/page/admin/presentation/policy-management/policies/policies.component.ts +++ b/frontend/src/app/modules/page/admin/presentation/policy-management/policies/policies.component.ts @@ -55,7 +55,7 @@ export class PoliciesComponent { label: 'actions.edit', icon: 'edit', action: (selectedPolicy: Record) => this.openEditView(selectedPolicy), - isAuthorized: data => this.roleService.isAdmin(), + isAuthorized: this.roleService.isAdmin(), } ], sortableColumns: { select: false, diff --git a/frontend/src/app/modules/shared/assembler/notificationMenuActions.assembler.ts b/frontend/src/app/modules/shared/assembler/notificationMenuActions.assembler.ts index 48d4959a22..b17df51a61 100644 --- a/frontend/src/app/modules/shared/assembler/notificationMenuActions.assembler.ts +++ b/frontend/src/app/modules/shared/assembler/notificationMenuActions.assembler.ts @@ -35,42 +35,49 @@ export class NotificationMenuActionsAssembler { icon: 'close', action: data => modal.show(NotificationStatus.CLOSED, data), condition: data => helperService.showCloseButton(data), - isAuthorized: data => helperService.isAuthorizedForButton(NotificationAction.CLOSE) && !notificationProcessingService.notificationIdsInLoadingState.has(data?.id), + isAuthorized: helperService.isAuthorizedForButton(NotificationAction.CLOSE), + isLoading: data => notificationProcessingService.isInLoadingProcess(data), }, { label: 'actions.approve', icon: 'share', action: data => modal.show(NotificationStatus.APPROVED, data), condition: data => helperService.showApproveButton(data), - isAuthorized: data => helperService.isAuthorizedForButton(NotificationAction.APPROVE) && !notificationProcessingService.notificationIdsInLoadingState.has(data?.id), + isAuthorized: helperService.isAuthorizedForButton(NotificationAction.APPROVE), + isLoading: data => notificationProcessingService.isInLoadingProcess(data), + }, { label: 'actions.cancel', icon: 'cancel', action: data => modal.show(NotificationStatus.CANCELED, data), condition: data => helperService.showCancelButton(data), - isAuthorized: data => helperService.isAuthorizedForButton(NotificationAction.CANCEL) && !notificationProcessingService.notificationIdsInLoadingState.has(data?.id), + isAuthorized: helperService.isAuthorizedForButton(NotificationAction.CANCEL), + isLoading: data => notificationProcessingService.isInLoadingProcess(data), }, { label: 'actions.acknowledge', icon: 'work', action: data => modal.show(NotificationStatus.ACKNOWLEDGED, data), condition: data => helperService.showAcknowledgeButton(data), - isAuthorized: data => helperService.isAuthorizedForButton(NotificationAction.ACKNOWLEDGE) && !notificationProcessingService.notificationIdsInLoadingState.has(data?.id), + isAuthorized: helperService.isAuthorizedForButton(NotificationAction.ACKNOWLEDGE), + isLoading: data => notificationProcessingService.isInLoadingProcess(data), }, { label: 'actions.accept', icon: 'assignment_turned_in', action: data => modal.show(NotificationStatus.ACCEPTED, data), condition: data => helperService.showAcceptButton(data), - isAuthorized: data => helperService.isAuthorizedForButton(NotificationAction.ACCEPT) && !notificationProcessingService.notificationIdsInLoadingState.has(data?.id), + isAuthorized: helperService.isAuthorizedForButton(NotificationAction.ACCEPT), + isLoading: data => notificationProcessingService.isInLoadingProcess(data), }, { label: 'actions.decline', icon: 'assignment_late', action: data => modal.show(NotificationStatus.DECLINED, data), condition: data => helperService.showDeclineButton(data), - isAuthorized: data => helperService.isAuthorizedForButton(NotificationAction.DECLINE) && !notificationProcessingService.notificationIdsInLoadingState.has(data?.id), + isAuthorized: helperService.isAuthorizedForButton(NotificationAction.DECLINE), + isLoading: data => notificationProcessingService.isInLoadingProcess(data), }, ]; } diff --git a/frontend/src/app/modules/shared/components/table/table.component.html b/frontend/src/app/modules/shared/components/table/table.component.html index cc6c96ab7b..9e3aa06f28 100644 --- a/frontend/src/app/modules/shared/components/table/table.component.html +++ b/frontend/src/app/modules/shared/components/table/table.component.html @@ -338,10 +338,12 @@

{{ 'table.noResultFound' | i18n }}

matTooltipPosition="above" [class.mdc-tooltip--multiline]="true" [matTooltipShowDelay]="1000" + [matTooltipDisabled]="config?.isAuthorized !== false && !config?.isLoading(row)" + [class.menu-action-button--unauthorized]="(config?.isAuthorized === false && config?.isLoading(row)) ? 'unauthorized' : ''" + (click)="(config?.isAuthorized === false && config?.isLoading(row)) ? null : config.action(row)" + onkeydown="(config?.isAuthorized === false && config?.isLoading(row)) ? null : config.action(row)" *ngIf="!config.condition || config.condition(row)" class="menu-action-button" - [class.menu-action-button--unauthorized]="(config?.isAuthorized(row) === false) ? 'unauthorized' : ''" - [attr.data-testId]="'table-menu-button--' + config.label" mat-menu-item > diff --git a/frontend/src/app/modules/shared/components/table/table.component.ts b/frontend/src/app/modules/shared/components/table/table.component.ts index 761d9bccd4..d752da3be0 100644 --- a/frontend/src/app/modules/shared/components/table/table.component.ts +++ b/frontend/src/app/modules/shared/components/table/table.component.ts @@ -55,7 +55,7 @@ import { import { ToastService } from '@shared/components/toasts/toast.service'; import { isDateFilter } from '@shared/helper/filter-helper'; import { addSelectedValues, clearAllRows, clearCurrentRows, removeSelectedValues } from '@shared/helper/table-helper'; -import { NotificationStatus } from '@shared/model/notification.model'; +import { Notification, NotificationStatus } from '@shared/model/notification.model'; import { FlattenObjectPipe } from '@shared/pipes/flatten-object.pipe'; import { NotificationProcessingService } from '@shared/service/notification-processing.service'; @@ -405,6 +405,7 @@ export class TableComponent { label: 'actions.viewDetails', icon: 'remove_red_eye', action: (data: Record) => this.selected.emit(data), + isLoading: (data: Notification) => this.notificationProcessingService.isInLoadingProcess(data), }; const editDetailsMenuAction: MenuActionConfig = { @@ -412,7 +413,8 @@ export class TableComponent { icon: 'edit', action: (data: Record) => this.editClicked.emit(data), condition: data => this.isEditable(data), - isAuthorized: data => this.roleService.isSupervisor() || this.roleService.isUser(), + isAuthorized: this.roleService.isSupervisor() || this.roleService.isUser(), + isLoading: (data: Notification) => this.notificationProcessingService.isInLoadingProcess(data), }; const defaultActionsToAdd: MenuActionConfig[] = [ viewDetailsMenuAction, editDetailsMenuAction ] .filter(action => !menuActionsConfig.some(a => a.label === action.label)); diff --git a/frontend/src/app/modules/shared/components/table/table.model.ts b/frontend/src/app/modules/shared/components/table/table.model.ts index 348ce8c91e..2d80b6eb86 100644 --- a/frontend/src/app/modules/shared/components/table/table.model.ts +++ b/frontend/src/app/modules/shared/components/table/table.model.ts @@ -55,5 +55,6 @@ export interface MenuActionConfig { icon: string; action: (data: T) => void; condition?: (data: T) => boolean; - isAuthorized?: (data: T) => boolean; + isAuthorized?: boolean; + isLoading?: (data: T) => boolean; } diff --git a/frontend/src/app/modules/shared/service/notification-processing.service.ts b/frontend/src/app/modules/shared/service/notification-processing.service.ts index eb05947ea8..5066c5ea8d 100644 --- a/frontend/src/app/modules/shared/service/notification-processing.service.ts +++ b/frontend/src/app/modules/shared/service/notification-processing.service.ts @@ -1,4 +1,5 @@ import { Injectable } from '@angular/core'; +import { Notification } from '@shared/model/notification.model'; /******************************************************************************** * Copyright (c) 2024 Contributors to the Eclipse Foundation @@ -23,4 +24,8 @@ import { Injectable } from '@angular/core'; }) export class NotificationProcessingService { notificationIdsInLoadingState: Set = new Set(); + + public isInLoadingProcess({ id } = {} as Notification): boolean { + return this.notificationIdsInLoadingState.has(id); + } } From b1eb670925f2a59528939fbedb6adc7f445156d2 Mon Sep 17 00:00:00 2001 From: Martin Maul Date: Wed, 10 Jul 2024 15:20:40 +0200 Subject: [PATCH 3/5] chore(notifications): 999 notification processing handler --- CHANGELOG.md | 5 +++ .../policies/policies.assembler.spec.ts | 1 - .../policies/policies.component.ts | 1 - .../policy-editor/policy-editor.component.ts | 1 - .../detail/notification-detail.component.html | 38 ++++++++-------- .../detail/notification-detail.component.ts | 9 +++- .../presentation/notifications.component.ts | 3 +- .../notificationMenuActions.assembler.ts | 4 -- .../notification-overview.component.html | 43 +++++++++++++------ .../notification-overview.component.ts | 7 +++ .../components/table/table.component.html | 10 ++--- .../components/table/table.component.ts | 4 +- .../shared/components/toasts/toast.service.ts | 4 +- .../notification-action-modal.component.ts | 3 -- .../notification-tab.component.html | 2 +- .../shared/service/notification.service.ts | 21 ++++++--- frontend/src/assets/locales/de/common.json | 4 +- frontend/src/assets/locales/en/common.json | 4 +- 18 files changed, 105 insertions(+), 59 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f1c9167baa..4bc8698a98 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,11 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). _**For better traceability add the corresponding GitHub issue number in each changelog entry, please.**_ ## [UNRELEASED - DD.MM.YYYY] + +### Added + +- #999 Added notification processing feedback in FE + ### Changed - #1173 Update IRS-Helm from 7.1.4 to 7.2.0 - updated Compatibility Matrix - #1082 fix duplicate key errors when synchronizing assets with IRS diff --git a/frontend/src/app/modules/page/admin/presentation/policy-management/policies/policies.assembler.spec.ts b/frontend/src/app/modules/page/admin/presentation/policy-management/policies/policies.assembler.spec.ts index 288a8bcc0b..0e59f02eec 100644 --- a/frontend/src/app/modules/page/admin/presentation/policy-management/policies/policies.assembler.spec.ts +++ b/frontend/src/app/modules/page/admin/presentation/policy-management/policies/policies.assembler.spec.ts @@ -79,7 +79,6 @@ const mockPolicyResponse: PolicyResponseMap = { describe('PoliciesAssembler', () => { it('should assemble policy', () => { const assembledPolicy = PoliciesAssembler.assemblePolicy(mockPolicy2); - console.log(assembledPolicy.constraints); expect(assembledPolicy.policyName).toBe(mockPolicy2.policyId); expect(assembledPolicy.createdOn).toBe('2024-01-01T00:00'); expect(assembledPolicy.validUntil).toBe('2024-12-31T23:59'); diff --git a/frontend/src/app/modules/page/admin/presentation/policy-management/policies/policies.component.ts b/frontend/src/app/modules/page/admin/presentation/policy-management/policies/policies.component.ts index 9bb3de39c3..0ecd770ec6 100644 --- a/frontend/src/app/modules/page/admin/presentation/policy-management/policies/policies.component.ts +++ b/frontend/src/app/modules/page/admin/presentation/policy-management/policies/policies.component.ts @@ -87,7 +87,6 @@ export class PoliciesComponent { this.selectedPoliciesInfoLabel = selectedPolicies.length === 1 ? 'pageAdmin.policyManagement.selectedPolicy' : 'pageAdmin.policyManagement.selectedPolicies'; this.selectedPolicies = selectedPolicies; this.isDefaultSelected = this.selectedPolicies.filter(policy => policy?.policyId === 'default-policy').length > 0; - console.log(this.isDefaultSelected); } openDetailedView(selectedPolicy: Record) { diff --git a/frontend/src/app/modules/page/admin/presentation/policy-management/policy-editor/policy-editor.component.ts b/frontend/src/app/modules/page/admin/presentation/policy-management/policy-editor/policy-editor.component.ts index 8136da9798..faac5cc379 100644 --- a/frontend/src/app/modules/page/admin/presentation/policy-management/policy-editor/policy-editor.component.ts +++ b/frontend/src/app/modules/page/admin/presentation/policy-management/policy-editor/policy-editor.component.ts @@ -74,7 +74,6 @@ export class PolicyEditorComponent { this.selectedPolicySubscription = this.policyFacade.selectedPolicy$.subscribe(next => { this.selectedPolicy = next?.data; if (next?.data) { - console.log(next.data); this.updatePolicyForm(this.selectedPolicy); } }); 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 60680a8679..7784f0cbf2 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 @@ -32,19 +32,19 @@
edit @@ -57,7 +57,7 @@ onkeydown="notificationCommonModalComponent?.show(NotificationStatus.APPROVED)" (click)="notificationCommonModalComponent?.show(NotificationStatus.APPROVED)" [color]="'primary'" - [isDisabled]="!actionHelperService.isAuthorizedForButton(NotificationAction.APPROVE)" + [isDisabled]="!actionHelperService.isAuthorizedForButton(NotificationAction.APPROVE) || isProcessing()" >
share @@ -66,19 +66,19 @@
cancel @@ -87,19 +87,19 @@
close @@ -108,19 +108,19 @@
assignment_turned_in @@ -129,19 +129,19 @@
work @@ -150,19 +150,19 @@
assignment_late 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 da17a6aba8..b472adc1a3 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 @@ -31,6 +31,7 @@ import { ToastService } from '@shared/components/toasts/toast.service'; import { Notification, NotificationStatus, NotificationType } from '@shared/model/notification.model'; import { View } from '@shared/model/view.model'; import { NotificationAction } from '@shared/modules/notification/notification-action.enum'; +import { NotificationProcessingService } from '@shared/service/notification-processing.service'; import { StaticIdService } from '@shared/service/staticId.service'; import { BehaviorSubject, Observable, Subject, Subscription } from 'rxjs'; import { filter, first, tap } from 'rxjs/operators'; @@ -78,18 +79,20 @@ export class NotificationDetailComponent implements AfterViewInit, OnDestroy { private router: Router, private readonly route: ActivatedRoute, private readonly toastService: ToastService, + private readonly notificationProcessingService: NotificationProcessingService, ) { this.notificationPartsInformation$ = this.notificationDetailFacade.notificationPartsInformation$; this.supplierPartsDetailInformation$ = this.notificationDetailFacade.supplierPartsInformation$; this.toastActionSubscription = this.toastService.retryAction.subscribe({ next: result => { - const formattedStatus = result?.context?.charAt(0)?.toUpperCase() + result?.context?.slice(1)?.toLowerCase(); + const formattedStatus = result?.context?.notificationStatus?.charAt(0)?.toUpperCase() + result?.context?.notificationStatus?.slice(1)?.toLowerCase(); if (result?.success) { this.toastService.success(`requestNotification.successfully${ formattedStatus }`); } else if (result?.error) { this.toastService.error(`requestNotification.failed${ formattedStatus }`, 15000, true); } + this.notificationProcessingService.notificationIdsInLoadingState.delete(result?.context?.notificationId); this.ngAfterViewInit(); }, }); @@ -228,6 +231,10 @@ export class NotificationDetailComponent implements AfterViewInit, OnDestroy { .subscribe(); } + isProcessing() { + return this.notificationProcessingService.isInLoadingProcess(this.selectedNotification); + } + protected readonly NotificationType = NotificationType; protected readonly NotificationAction = NotificationAction; 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 38288aa7a4..29619ea054 100644 --- a/frontend/src/app/modules/page/notifications/presentation/notifications.component.ts +++ b/frontend/src/app/modules/page/notifications/presentation/notifications.component.ts @@ -89,13 +89,14 @@ export class NotificationsComponent { this.toastActionSubscription = this.toastService.retryAction.subscribe({ next: result => { - const formatted = result?.context?.charAt(0)?.toUpperCase() + result?.context?.slice(1)?.toLowerCase(); + const formatted = result?.context?.notificationStatus?.charAt(0)?.toUpperCase() + result?.context?.notificationStatus?.slice(1)?.toLowerCase(); if (result?.success) { this.toastService.success(`requestNotification.successfully${ formatted }`); } else if (result?.error) { this.toastService.error(`requestNotification.failed${ formatted }`, 15000, true); } this.handleConfirmActionCompletedEvent(); + this.notificationProcessingService.notificationIdsInLoadingState.delete(result?.context?.notificationId); }, }); } diff --git a/frontend/src/app/modules/shared/assembler/notificationMenuActions.assembler.ts b/frontend/src/app/modules/shared/assembler/notificationMenuActions.assembler.ts index b17df51a61..5d5da004c2 100644 --- a/frontend/src/app/modules/shared/assembler/notificationMenuActions.assembler.ts +++ b/frontend/src/app/modules/shared/assembler/notificationMenuActions.assembler.ts @@ -25,10 +25,6 @@ import { NotificationProcessingService } from '@shared/service/notification-proc export class NotificationMenuActionsAssembler { public static getMenuActions(helperService: NotificationActionHelperService, modal: NotificationCommonModalComponent, notificationProcessingService: NotificationProcessingService): MenuActionConfig[] { - const notInLoadingState = (id: string) => { - console.log(id); - return !notificationProcessingService.notificationIdsInLoadingState.has(id); - }; return [ { label: 'actions.close', diff --git a/frontend/src/app/modules/shared/components/notification-overview/notification-overview.component.html b/frontend/src/app/modules/shared/components/notification-overview/notification-overview.component.html index c598471174..ff4b04a479 100644 --- a/frontend/src/app/modules/shared/components/notification-overview/notification-overview.component.html +++ b/frontend/src/app/modules/shared/components/notification-overview/notification-overview.component.html @@ -32,18 +32,16 @@
{{ 'table.column.description' | i18n }} - {{ - notification?.description + {{ + notification?.description }}
{{ 'table.column.status' | i18n }} - - {{ 'commonAlert.status.' + notification?.status | i18n }} - + + +
{{ 'table.column.createdDate' | i18n }} @@ -58,25 +56,25 @@
{{ 'table.column.createdBy' | i18n }} - {{notification.createdBy}} + {{ notification.createdBy }}
{{ 'table.column.createdByName' | i18n }} - {{notification.createdByName}} + {{ notification.createdByName }}
{{ 'table.column.sendTo' | i18n }} - {{notification.sendTo}} + {{ notification.sendTo }}
{{ 'table.column.sendToName' | i18n }} - {{notification.sendToName}} + {{ notification.sendToName }}
@@ -111,3 +109,24 @@ {{ date }} + + + + {{ 'commonAlert.status.' + notification?.status | i18n }} + + + + + + + + diff --git a/frontend/src/app/modules/shared/components/notification-overview/notification-overview.component.ts b/frontend/src/app/modules/shared/components/notification-overview/notification-overview.component.ts index c30cae0326..a9adbf9eac 100644 --- a/frontend/src/app/modules/shared/components/notification-overview/notification-overview.component.ts +++ b/frontend/src/app/modules/shared/components/notification-overview/notification-overview.component.ts @@ -21,6 +21,8 @@ import { Component, Input } from '@angular/core'; import { Notification } from '@shared/model/notification.model'; +import { NotificationAction } from '@shared/modules/notification/notification-action.enum'; +import { NotificationProcessingService } from '@shared/service/notification-processing.service'; @Component({ selector: 'app-notification-overview', @@ -34,4 +36,9 @@ import { Notification } from '@shared/model/notification.model'; export class NotificationOverviewComponent { @Input() notification: Notification; @Input() showNotification = true; + + constructor(public notificationProcessingService: NotificationProcessingService) { + } + + protected readonly NotificationAction = NotificationAction; } diff --git a/frontend/src/app/modules/shared/components/table/table.component.html b/frontend/src/app/modules/shared/components/table/table.component.html index 9e3aa06f28..f75e98219a 100644 --- a/frontend/src/app/modules/shared/components/table/table.component.html +++ b/frontend/src/app/modules/shared/components/table/table.component.html @@ -333,15 +333,15 @@

{{ 'table.noResultFound' | i18n }}