Browse Source

Added ability to reassing alarm in alarm details dialog

pull/7942/head
rusikv 4 years ago
parent
commit
36b987cb5f
  1. 3
      ui-ngx/src/app/modules/home/components/alarm/alarm-assignee-panel.component.scss
  2. 41
      ui-ngx/src/app/modules/home/components/alarm/alarm-assignee.component.html
  3. 51
      ui-ngx/src/app/modules/home/components/alarm/alarm-assignee.component.scss
  4. 141
      ui-ngx/src/app/modules/home/components/alarm/alarm-assignee.component.ts
  5. 12
      ui-ngx/src/app/modules/home/components/alarm/alarm-details-dialog.component.html
  6. 64
      ui-ngx/src/app/modules/home/components/alarm/alarm-details-dialog.component.ts
  7. 7
      ui-ngx/src/app/modules/home/components/shared-home-components.module.ts

3
ui-ngx/src/app/modules/home/components/alarm/alarm-assignee-panel.component.scss

@ -65,7 +65,6 @@
display: inline-flex; display: inline-flex;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
align-self: center;
margin-right: 8px; margin-right: 8px;
border-radius: 50%; border-radius: 50%;
background-color: #5cb445; background-color: #5cb445;
@ -73,7 +72,7 @@
height: 28px; height: 28px;
min-width: 28px; min-width: 28px;
min-height: 28px; min-height: 28px;
color: white; color: #fff;
font-size: 13px; font-size: 13px;
font-weight: 700 font-weight: 700
} }

41
ui-ngx/src/app/modules/home/components/alarm/alarm-assignee.component.html

@ -0,0 +1,41 @@
<!--
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.
-->
<div class="tb-assignee" fxLayout="row" fxLayoutAlign="start center"
(click)="openAlarmAssigneePanel($event, alarm)">
<span *ngIf="alarm.assigneeId" class="assigned-container">
<span class="user-avatar" [style.backgroundColor]="getAvatarBgColor(alarm.assignee)">
{{ getUserInitials(alarm.assignee) }}
</span>
<span [matTooltip]="getUserDisplayName(alarm.assignee)"
matTooltipPosition="above"
style="text-overflow: ellipsis">{{ getUserDisplayName(alarm.assignee) }}</span>
</span>
<span *ngIf="!alarm.assigneeId" class="unassigned-container" fxLayout="row" fxLayoutAlign="start center">
<mat-icon class="material-icons unassigned-icon">account_circle</mat-icon>
<span translate>alarm.unassigned</span>
</span>
<button fxFlexAlign="flex-end"
mat-icon-button
matTooltip="{{ 'alarm.assign' | translate }}"
matTooltipPosition="above">
<mat-icon>
keyboard_arrow_down
</mat-icon>
</button>
</div>

51
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;
}
}
}

141
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<boolean>();
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));
}
}

12
ui-ngx/src/app/modules/home/components/alarm/alarm-details-dialog.component.html

@ -56,14 +56,10 @@
<mat-label translate>alarm.status</mat-label> <mat-label translate>alarm.status</mat-label>
<input matInput formControlName="alarmStatus" readonly> <input matInput formControlName="alarmStatus" readonly>
</mat-form-field> </mat-form-field>
<mat-form-field *ngIf="alarmFormGroup.get('assignee').value" appearance="none" fxFlex class="mat-block assignee-field"> <tb-alarm-assignee fxFlex style="padding-top: 9px"
<mat-label translate="">alarm.assignee</mat-label> [alarm]="alarm$ | async"
<span matPrefix fxLayout="row" fxLayoutAlign="center center" class="user-avatar" (alarmReassigned)="onReassign()">
[style.background-color]="getColorFromString(alarmFormGroup.get('assignee').value)"> </tb-alarm-assignee>
{{ assigneeInitials }}
</span>
<input matInput formControlName="assignee" readonly>
</mat-form-field>
</div> </div>
<mat-expansion-panel class="tb-alarm-details"> <mat-expansion-panel class="tb-alarm-details">
<mat-expansion-panel-header fxLayout="row wrap"> <mat-expansion-panel-header fxLayout="row wrap">

64
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 { DatePipe } from '@angular/common';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { AlarmCommentComponent } from '@home/components/alarm/alarm-comment.component'; import { AlarmCommentComponent } from '@home/components/alarm/alarm-comment.component';
import { UtilsService } from '@core/services/utils.service';
export interface AlarmDetailsDialogData { export interface AlarmDetailsDialogData {
alarmId?: string; alarmId?: string;
@ -70,8 +69,6 @@ export class AlarmDetailsDialogComponent extends DialogComponent<AlarmDetailsDia
@ViewChild('alarmCommentComponent', { static: true }) alarmCommentComponent: AlarmCommentComponent; @ViewChild('alarmCommentComponent', { static: true }) alarmCommentComponent: AlarmCommentComponent;
assigneeInitials = '';
constructor(protected store: Store<AppState>, constructor(protected store: Store<AppState>,
protected router: Router, protected router: Router,
private datePipe: DatePipe, private datePipe: DatePipe,
@ -79,8 +76,7 @@ export class AlarmDetailsDialogComponent extends DialogComponent<AlarmDetailsDia
@Inject(MAT_DIALOG_DATA) public data: AlarmDetailsDialogData, @Inject(MAT_DIALOG_DATA) public data: AlarmDetailsDialogData,
private alarmService: AlarmService, private alarmService: AlarmService,
public dialogRef: MatDialogRef<AlarmDetailsDialogComponent, boolean>, public dialogRef: MatDialogRef<AlarmDetailsDialogComponent, boolean>,
public fb: UntypedFormBuilder, public fb: UntypedFormBuilder) {
private utilsService: UtilsService) {
super(store, router, dialogRef); super(store, router, dialogRef);
this.allowAcknowledgment = data.allowAcknowledgment; this.allowAcknowledgment = data.allowAcknowledgment;
@ -91,15 +87,12 @@ export class AlarmDetailsDialogComponent extends DialogComponent<AlarmDetailsDia
{ {
createdTime: [''], createdTime: [''],
originatorName: [''], originatorName: [''],
assigneeId: [''],
startTime: [''], startTime: [''],
endTime: [''], endTime: [''],
ackTime: [''], ackTime: [''],
clearTime: [''], clearTime: [''],
assignTime: [''],
type: [''], type: [''],
alarmSeverity: [''], alarmSeverity: [''],
assignee: [''],
alarmStatus: [''], alarmStatus: [''],
alarmDetails: [null] alarmDetails: [null]
} }
@ -125,10 +118,6 @@ export class AlarmDetailsDialogComponent extends DialogComponent<AlarmDetailsDia
.patchValue(this.datePipe.transform(alarm.createdTime, 'yyyy-MM-dd HH:mm:ss')); .patchValue(this.datePipe.transform(alarm.createdTime, 'yyyy-MM-dd HH:mm:ss'));
this.alarmFormGroup.get('originatorName') this.alarmFormGroup.get('originatorName')
.patchValue(alarm.originatorName); .patchValue(alarm.originatorName);
if(alarm.assigneeId) {
this.alarmFormGroup.get('assigneeId')
.patchValue(alarm.assigneeId.id);
}
if (alarm.startTs) { if (alarm.startTs) {
this.alarmFormGroup.get('startTime') this.alarmFormGroup.get('startTime')
.patchValue(this.datePipe.transform(alarm.startTs, 'yyyy-MM-dd HH:mm:ss')); .patchValue(this.datePipe.transform(alarm.startTs, 'yyyy-MM-dd HH:mm:ss'));
@ -145,20 +134,11 @@ export class AlarmDetailsDialogComponent extends DialogComponent<AlarmDetailsDia
this.alarmFormGroup.get('clearTime') this.alarmFormGroup.get('clearTime')
.patchValue(this.datePipe.transform(alarm.clearTs, 'yyyy-MM-dd HH:mm:ss')); .patchValue(this.datePipe.transform(alarm.clearTs, 'yyyy-MM-dd HH:mm:ss'));
} }
if (alarm.assignTs) {
this.alarmFormGroup.get('assignTime')
.patchValue(this.datePipe.transform(alarm.assignTs, 'yyyy-MM-dd HH:mm:ss'));
}
this.alarmFormGroup.get('type').patchValue(alarm.type); this.alarmFormGroup.get('type').patchValue(alarm.type);
this.alarmFormGroup.get('alarmSeverity') this.alarmFormGroup.get('alarmSeverity')
.patchValue(this.translate.instant(alarmSeverityTranslations.get(alarm.severity))); .patchValue(this.translate.instant(alarmSeverityTranslations.get(alarm.severity)));
this.alarmFormGroup.get('alarmStatus') this.alarmFormGroup.get('alarmStatus')
.patchValue(this.translate.instant(alarmStatusTranslations.get(alarm.status))); .patchValue(this.translate.instant(alarmStatusTranslations.get(alarm.status)));
if (alarm.assigneeId) {
this.alarmFormGroup.get('assignee').
patchValue(this.getUserDisplayName(alarm));
this.assigneeInitials = this.getUserInitials(alarm);
}
this.alarmFormGroup.get('alarmDetails').patchValue(alarm.details); this.alarmFormGroup.get('alarmDetails').patchValue(alarm.details);
} }
@ -193,43 +173,9 @@ export class AlarmDetailsDialogComponent extends DialogComponent<AlarmDetailsDia
} }
} }
getColorFromString(userDisplayName: string) { onReassign(): void {
return this.utilsService.stringToHslColor(userDisplayName, 40, 60); this.alarmUpdated = true;
} this.loadAlarm()
this.alarmCommentComponent.loadAlarmComments();
getUserDisplayName(entity: AlarmInfo) {
let displayName = '';
if ((entity.assignee.firstName && entity.assignee.firstName.length > 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();
} }
} }

7
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 { SHARED_HOME_COMPONENTS_MODULE_TOKEN } from '@home/components/tokens';
import { AlarmCommentComponent } from '@home/components/alarm/alarm-comment.component'; import { AlarmCommentComponent } from '@home/components/alarm/alarm-comment.component';
import { AlarmCommentDialogComponent } from '@home/components/alarm/alarm-comment-dialog.component'; import { AlarmCommentDialogComponent } from '@home/components/alarm/alarm-comment-dialog.component';
import { AlarmAssigneeComponent } from '@home/components/alarm/alarm-assignee.component';
@NgModule({ @NgModule({
providers: [ providers: [
@ -30,7 +31,8 @@ import { AlarmCommentDialogComponent } from '@home/components/alarm/alarm-commen
[ [
AlarmDetailsDialogComponent, AlarmDetailsDialogComponent,
AlarmCommentComponent, AlarmCommentComponent,
AlarmCommentDialogComponent AlarmCommentDialogComponent,
AlarmAssigneeComponent
], ],
imports: [ imports: [
CommonModule, CommonModule,
@ -39,7 +41,8 @@ import { AlarmCommentDialogComponent } from '@home/components/alarm/alarm-commen
exports: [ exports: [
AlarmDetailsDialogComponent, AlarmDetailsDialogComponent,
AlarmCommentComponent, AlarmCommentComponent,
AlarmCommentDialogComponent AlarmCommentDialogComponent,
AlarmAssigneeComponent
] ]
}) })
export class SharedHomeComponentsModule { } export class SharedHomeComponentsModule { }

Loading…
Cancel
Save