From 36b987cb5f1e59497a1686b6cae94a0ec6bed2bd Mon Sep 17 00:00:00 2001 From: rusikv Date: Mon, 27 Feb 2023 13:05:05 +0200 Subject: [PATCH] Added ability to reassing alarm in alarm details dialog --- .../alarm/alarm-assignee-panel.component.scss | 3 +- .../alarm/alarm-assignee.component.html | 41 +++++ .../alarm/alarm-assignee.component.scss | 51 +++++++ .../alarm/alarm-assignee.component.ts | 141 ++++++++++++++++++ .../alarm/alarm-details-dialog.component.html | 12 +- .../alarm/alarm-details-dialog.component.ts | 64 +------- .../shared-home-components.module.ts | 7 +- 7 files changed, 248 insertions(+), 71 deletions(-) create mode 100644 ui-ngx/src/app/modules/home/components/alarm/alarm-assignee.component.html create mode 100644 ui-ngx/src/app/modules/home/components/alarm/alarm-assignee.component.scss create mode 100644 ui-ngx/src/app/modules/home/components/alarm/alarm-assignee.component.ts diff --git a/ui-ngx/src/app/modules/home/components/alarm/alarm-assignee-panel.component.scss b/ui-ngx/src/app/modules/home/components/alarm/alarm-assignee-panel.component.scss index 971e0abd55..8db3bb111f 100644 --- a/ui-ngx/src/app/modules/home/components/alarm/alarm-assignee-panel.component.scss +++ b/ui-ngx/src/app/modules/home/components/alarm/alarm-assignee-panel.component.scss @@ -65,7 +65,6 @@ display: inline-flex; justify-content: center; align-items: center; - align-self: center; margin-right: 8px; border-radius: 50%; background-color: #5cb445; @@ -73,7 +72,7 @@ height: 28px; min-width: 28px; min-height: 28px; - color: white; + color: #fff; font-size: 13px; font-weight: 700 } diff --git a/ui-ngx/src/app/modules/home/components/alarm/alarm-assignee.component.html b/ui-ngx/src/app/modules/home/components/alarm/alarm-assignee.component.html new file mode 100644 index 0000000000..6e50082e8b --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/alarm/alarm-assignee.component.html @@ -0,0 +1,41 @@ + + +
+ + + {{ getUserInitials(alarm.assignee) }} + + {{ getUserDisplayName(alarm.assignee) }} + + + account_circle + alarm.unassigned + + +
diff --git a/ui-ngx/src/app/modules/home/components/alarm/alarm-assignee.component.scss b/ui-ngx/src/app/modules/home/components/alarm/alarm-assignee.component.scss new file mode 100644 index 0000000000..5736aa8ef1 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/alarm/alarm-assignee.component.scss @@ -0,0 +1,51 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://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. + */ + +:host { + .tb-assignee { + cursor: pointer; + + .assigned-container { + max-width: 233px; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + + .user-avatar { + display: inline-flex; + justify-content: center; + align-items: center; + margin-right: 8px; + border-radius: 50%; + width: 28px; + height: 28px; + min-width: 28px; + min-height: 28px; + color: white; + font-size: 13px; + font-weight: 700; + } + } + .material-icons.unassigned-icon { + width: 28px; + height: 28px; + font-size: 28px; + margin-right: 8px; + color: rgba(0, 0, 0, 0.38); + overflow: visible; + } + } +} diff --git a/ui-ngx/src/app/modules/home/components/alarm/alarm-assignee.component.ts b/ui-ngx/src/app/modules/home/components/alarm/alarm-assignee.component.ts new file mode 100644 index 0000000000..5d7f29d55b --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/alarm/alarm-assignee.component.ts @@ -0,0 +1,141 @@ +/// +/// Copyright © 2016-2023 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://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. +/// + +import { + Component, EventEmitter, Injector, Input, Output, StaticProvider, ViewContainerRef +} from '@angular/core'; +import { UtilsService } from '@core/services/utils.service'; +import { AlarmAssignee, AlarmInfo } from '@shared/models/alarm.models'; +import { + ALARM_ASSIGNEE_PANEL_DATA, AlarmAssigneePanelComponent, + AlarmAssigneePanelData +} from '@home/components/alarm/alarm-assignee-panel.component'; +import { ConnectedPosition, Overlay, OverlayConfig, OverlayRef } from '@angular/cdk/overlay'; +import { ComponentPortal } from '@angular/cdk/portal'; + +@Component({ + selector: 'tb-alarm-assignee', + templateUrl: './alarm-assignee.component.html', + styleUrls: ['./alarm-assignee.component.scss'] +}) +export class AlarmAssigneeComponent { + @Input() + alarm: AlarmInfo; + + @Output() + alarmReassigned = new EventEmitter(); + + constructor(private utilsService: UtilsService, + private overlay: Overlay, + private viewContainerRef: ViewContainerRef) { + } + + getUserDisplayName(entity: AlarmAssignee) { + let displayName = ''; + if ((entity.firstName && entity.firstName.length > 0) || + (entity.lastName && entity.lastName.length > 0)) { + if (entity.firstName) { + displayName += entity.firstName; + } + if (entity.lastName) { + if (displayName.length > 0) { + displayName += ' '; + } + displayName += entity.lastName; + } + } else { + displayName = entity.email; + } + return displayName; + } + + getUserInitials(entity: AlarmAssignee): string { + let initials = ''; + if (entity.firstName && entity.firstName.length || + entity.lastName && entity.lastName.length) { + if (entity.firstName) { + initials += entity.firstName.charAt(0); + } + if (entity.lastName) { + initials += entity.lastName.charAt(0); + } + } else { + initials += entity.email.charAt(0); + } + return initials.toUpperCase(); + } + + getFullName(entity: AlarmAssignee): string { + let fullName = ''; + if ((entity.firstName && entity.firstName.length > 0) || + (entity.lastName && entity.lastName.length > 0)) { + if (entity.firstName) { + fullName += entity.firstName; + } + if (entity.lastName) { + if (fullName.length > 0) { + fullName += ' '; + } + fullName += entity.lastName; + } + } + return fullName; + } + + getAvatarBgColor(entity: AlarmAssignee) { + return this.utilsService.stringToHslColor(this.getUserDisplayName(entity), 40, 60); + } + + openAlarmAssigneePanel($event: Event, alarm: AlarmInfo) { + if ($event) { + $event.stopPropagation(); + } + const target = $event.target || $event.srcElement || $event.currentTarget; + const config = new OverlayConfig(); + config.backdropClass = 'cdk-overlay-transparent-backdrop'; + config.hasBackdrop = true; + const connectedPosition: ConnectedPosition = { + originX: 'end', + originY: 'bottom', + overlayX: 'end', + overlayY: 'top' + }; + config.positionStrategy = this.overlay.position().flexibleConnectedTo(target as HTMLElement) + .withPositions([connectedPosition]); + config.minWidth = '260px'; + const overlayRef = this.overlay.create(config); + overlayRef.backdropClick().subscribe(() => { + overlayRef.dispose(); + }); + const providers: StaticProvider[] = [ + { + provide: ALARM_ASSIGNEE_PANEL_DATA, + useValue: { + alarmId: alarm.id.id, + assigneeId: alarm.assigneeId?.id + } as AlarmAssigneePanelData + }, + { + provide: OverlayRef, + useValue: overlayRef + } + ]; + const injector = Injector.create({parent: this.viewContainerRef.injector, providers}); + overlayRef.attach(new ComponentPortal(AlarmAssigneePanelComponent, + this.viewContainerRef, injector)).onDestroy(() => this.alarmReassigned.emit(true)); + } + +} diff --git a/ui-ngx/src/app/modules/home/components/alarm/alarm-details-dialog.component.html b/ui-ngx/src/app/modules/home/components/alarm/alarm-details-dialog.component.html index c8bbbe5537..c1be93431d 100644 --- a/ui-ngx/src/app/modules/home/components/alarm/alarm-details-dialog.component.html +++ b/ui-ngx/src/app/modules/home/components/alarm/alarm-details-dialog.component.html @@ -56,14 +56,10 @@ alarm.status - - alarm.assignee - - {{ assigneeInitials }} - - - + + diff --git a/ui-ngx/src/app/modules/home/components/alarm/alarm-details-dialog.component.ts b/ui-ngx/src/app/modules/home/components/alarm/alarm-details-dialog.component.ts index a8d24bdafa..e86b470566 100644 --- a/ui-ngx/src/app/modules/home/components/alarm/alarm-details-dialog.component.ts +++ b/ui-ngx/src/app/modules/home/components/alarm/alarm-details-dialog.component.ts @@ -34,7 +34,6 @@ import { tap } from 'rxjs/operators'; import { DatePipe } from '@angular/common'; import { TranslateService } from '@ngx-translate/core'; import { AlarmCommentComponent } from '@home/components/alarm/alarm-comment.component'; -import { UtilsService } from '@core/services/utils.service'; export interface AlarmDetailsDialogData { alarmId?: string; @@ -70,8 +69,6 @@ export class AlarmDetailsDialogComponent extends DialogComponent, protected router: Router, private datePipe: DatePipe, @@ -79,8 +76,7 @@ export class AlarmDetailsDialogComponent extends DialogComponent, - public fb: UntypedFormBuilder, - private utilsService: UtilsService) { + public fb: UntypedFormBuilder) { super(store, router, dialogRef); this.allowAcknowledgment = data.allowAcknowledgment; @@ -91,15 +87,12 @@ export class AlarmDetailsDialogComponent extends DialogComponent 0) || - (entity.assignee.lastName && entity.assignee.lastName.length > 0)) { - if (entity.assignee.firstName) { - displayName += entity.assignee.firstName; - } - if (entity.assignee.lastName) { - if (displayName.length > 0) { - displayName += ' '; - } - displayName += entity.assignee.lastName; - } - } else { - displayName = entity.assignee.email; - } - return displayName; - } - - getUserInitials(entity: AlarmInfo): string { - let initials = ''; - if (entity.assignee.firstName && entity.assignee.firstName.length || - entity.assignee.lastName && entity.assignee.lastName.length) { - if (entity.assignee.firstName) { - initials += entity.assignee.firstName.charAt(0); - } - if (entity.assignee.lastName) { - initials += entity.assignee.lastName.charAt(0); - } - } else { - initials += entity.assignee.email.charAt(0); - } - return initials.toUpperCase(); + onReassign(): void { + this.alarmUpdated = true; + this.loadAlarm() + this.alarmCommentComponent.loadAlarmComments(); } - } diff --git a/ui-ngx/src/app/modules/home/components/shared-home-components.module.ts b/ui-ngx/src/app/modules/home/components/shared-home-components.module.ts index fbfc297439..865e7cf224 100644 --- a/ui-ngx/src/app/modules/home/components/shared-home-components.module.ts +++ b/ui-ngx/src/app/modules/home/components/shared-home-components.module.ts @@ -21,6 +21,7 @@ import { AlarmDetailsDialogComponent } from '@home/components/alarm/alarm-detail import { SHARED_HOME_COMPONENTS_MODULE_TOKEN } from '@home/components/tokens'; import { AlarmCommentComponent } from '@home/components/alarm/alarm-comment.component'; import { AlarmCommentDialogComponent } from '@home/components/alarm/alarm-comment-dialog.component'; +import { AlarmAssigneeComponent } from '@home/components/alarm/alarm-assignee.component'; @NgModule({ providers: [ @@ -30,7 +31,8 @@ import { AlarmCommentDialogComponent } from '@home/components/alarm/alarm-commen [ AlarmDetailsDialogComponent, AlarmCommentComponent, - AlarmCommentDialogComponent + AlarmCommentDialogComponent, + AlarmAssigneeComponent ], imports: [ CommonModule, @@ -39,7 +41,8 @@ import { AlarmCommentDialogComponent } from '@home/components/alarm/alarm-commen exports: [ AlarmDetailsDialogComponent, AlarmCommentComponent, - AlarmCommentDialogComponent + AlarmCommentDialogComponent, + AlarmAssigneeComponent ] }) export class SharedHomeComponentsModule { }