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

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>
<input matInput formControlName="alarmStatus" readonly>
</mat-form-field>
<mat-form-field *ngIf="alarmFormGroup.get('assignee').value" appearance="none" fxFlex class="mat-block assignee-field">
<mat-label translate="">alarm.assignee</mat-label>
<span matPrefix fxLayout="row" fxLayoutAlign="center center" class="user-avatar"
[style.background-color]="getColorFromString(alarmFormGroup.get('assignee').value)">
{{ assigneeInitials }}
</span>
<input matInput formControlName="assignee" readonly>
</mat-form-field>
<tb-alarm-assignee fxFlex style="padding-top: 9px"
[alarm]="alarm$ | async"
(alarmReassigned)="onReassign()">
</tb-alarm-assignee>
</div>
<mat-expansion-panel class="tb-alarm-details">
<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 { 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<AlarmDetailsDia
@ViewChild('alarmCommentComponent', { static: true }) alarmCommentComponent: AlarmCommentComponent;
assigneeInitials = '';
constructor(protected store: Store<AppState>,
protected router: Router,
private datePipe: DatePipe,
@ -79,8 +76,7 @@ export class AlarmDetailsDialogComponent extends DialogComponent<AlarmDetailsDia
@Inject(MAT_DIALOG_DATA) public data: AlarmDetailsDialogData,
private alarmService: AlarmService,
public dialogRef: MatDialogRef<AlarmDetailsDialogComponent, boolean>,
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<AlarmDetailsDia
{
createdTime: [''],
originatorName: [''],
assigneeId: [''],
startTime: [''],
endTime: [''],
ackTime: [''],
clearTime: [''],
assignTime: [''],
type: [''],
alarmSeverity: [''],
assignee: [''],
alarmStatus: [''],
alarmDetails: [null]
}
@ -125,10 +118,6 @@ export class AlarmDetailsDialogComponent extends DialogComponent<AlarmDetailsDia
.patchValue(this.datePipe.transform(alarm.createdTime, 'yyyy-MM-dd HH:mm:ss'));
this.alarmFormGroup.get('originatorName')
.patchValue(alarm.originatorName);
if(alarm.assigneeId) {
this.alarmFormGroup.get('assigneeId')
.patchValue(alarm.assigneeId.id);
}
if (alarm.startTs) {
this.alarmFormGroup.get('startTime')
.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')
.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('alarmSeverity')
.patchValue(this.translate.instant(alarmSeverityTranslations.get(alarm.severity)));
this.alarmFormGroup.get('alarmStatus')
.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);
}
@ -193,43 +173,9 @@ export class AlarmDetailsDialogComponent extends DialogComponent<AlarmDetailsDia
}
}
getColorFromString(userDisplayName: string) {
return this.utilsService.stringToHslColor(userDisplayName, 40, 60);
}
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();
onReassign(): void {
this.alarmUpdated = true;
this.loadAlarm()
this.alarmCommentComponent.loadAlarmComments();
}
}

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 { 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 { }

Loading…
Cancel
Save