Browse Source

Alarm details redesign

pull/8285/head
rusikv 4 years ago
parent
commit
92c2d4f021
  1. 2
      application/src/main/data/json/system/widget_bundles/alarm_widgets.json
  2. 3
      ui-ngx/src/app/modules/home/components/alarm/alarm-assignee-panel.component.html
  3. 2
      ui-ngx/src/app/modules/home/components/alarm/alarm-assignee-panel.component.scss
  4. 30
      ui-ngx/src/app/modules/home/components/alarm/alarm-assignee.component.html
  5. 55
      ui-ngx/src/app/modules/home/components/alarm/alarm-assignee.component.scss
  6. 92
      ui-ngx/src/app/modules/home/components/alarm/alarm-assignee.component.ts
  7. 17
      ui-ngx/src/app/modules/home/components/alarm/alarm-comment-dialog.component.html
  8. 4
      ui-ngx/src/app/modules/home/components/alarm/alarm-comment-dialog.component.ts
  9. 241
      ui-ngx/src/app/modules/home/components/alarm/alarm-comment.component.html
  10. 150
      ui-ngx/src/app/modules/home/components/alarm/alarm-comment.component.scss
  11. 25
      ui-ngx/src/app/modules/home/components/alarm/alarm-comment.component.ts
  12. 68
      ui-ngx/src/app/modules/home/components/alarm/alarm-details-dialog.component.html
  13. 40
      ui-ngx/src/app/modules/home/components/alarm/alarm-details-dialog.component.ts
  14. 5
      ui-ngx/src/app/modules/home/components/alarm/alarm-table-config.ts
  15. 66
      ui-ngx/src/app/modules/home/components/alarm/alarm-table.component.scss
  16. 26
      ui-ngx/src/app/modules/home/components/widget/lib/alarms-table-widget.component.ts
  17. 4
      ui-ngx/src/app/modules/home/components/widget/lib/settings/alarm/alarms-table-widget-settings.component.html
  18. 2
      ui-ngx/src/app/modules/home/components/widget/lib/settings/alarm/alarms-table-widget-settings.component.ts
  19. 13
      ui-ngx/src/assets/locale/locale.constant-en_US.json

2
application/src/main/data/json/system/widget_bundles/alarm_widgets.json

@ -23,7 +23,7 @@
"dataKeySettingsSchema": "",
"settingsDirective": "tb-alarms-table-widget-settings",
"dataKeySettingsDirective": "tb-alarms-table-key-settings",
"defaultConfig": "{\"timewindow\":{\"realtime\":{\"interval\":1000,\"timewindowMs\":86400000},\"aggregation\":{\"type\":\"NONE\",\"limit\":200}},\"showTitle\":true,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"4px\",\"settings\":{\"enableSelection\":true,\"enableSearch\":true,\"displayDetails\":true,\"allowAcknowledgment\":true,\"allowClear\":true,\"allowAssign\":true,\"displayComments\":true,\"displayPagination\":true,\"defaultPageSize\":10,\"defaultSortOrder\":\"-createdTime\",\"enableSelectColumnDisplay\":true,\"enableStickyAction\":false,\"enableFilter\":true},\"title\":\"Alarms table\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400,\"padding\":\"5px 10px 5px 10px\"},\"useDashboardTimewindow\":false,\"showLegend\":false,\"alarmSource\":{\"type\":\"function\",\"dataKeys\":[{\"name\":\"createdTime\",\"type\":\"alarm\",\"label\":\"Created time\",\"color\":\"#2196f3\",\"settings\":{\"useCellStyleFunction\":false,\"cellStyleFunction\":\"\",\"useCellContentFunction\":false,\"cellContentFunction\":\"\"},\"_hash\":0.021092237451093787},{\"name\":\"originator\",\"type\":\"alarm\",\"label\":\"Originator\",\"color\":\"#4caf50\",\"settings\":{\"useCellStyleFunction\":false,\"cellStyleFunction\":\"\",\"useCellContentFunction\":false,\"cellContentFunction\":\"\"},\"_hash\":0.2780007688856758},{\"name\":\"type\",\"type\":\"alarm\",\"label\":\"Type\",\"color\":\"#f44336\",\"settings\":{\"useCellStyleFunction\":false,\"cellStyleFunction\":\"\",\"useCellContentFunction\":false,\"cellContentFunction\":\"\"},\"_hash\":0.7323586880398418},{\"name\":\"severity\",\"type\":\"alarm\",\"label\":\"Severity\",\"color\":\"#ffc107\",\"settings\":{\"useCellStyleFunction\":false,\"useCellContentFunction\":false},\"_hash\":0.09927019860088193},{\"name\":\"status\",\"type\":\"alarm\",\"label\":\"Status\",\"color\":\"#607d8b\",\"settings\":{\"useCellStyleFunction\":false,\"cellStyleFunction\":\"\",\"useCellContentFunction\":false,\"cellContentFunction\":\"\"},\"_hash\":0.6588418951443418},{\"name\":\"assignee\",\"type\":\"alarm\",\"label\":\"Assignee\",\"color\":\"#9c27b0\",\"settings\":{},\"_hash\":0.5008441077416634}],\"entityAliasId\":null,\"name\":\"alarms\"},\"alarmSearchStatus\":\"ANY\",\"alarmsPollingInterval\":5,\"showTitleIcon\":false,\"titleIcon\":\"more_horiz\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"displayTimewindow\":true,\"actions\":{},\"alarmStatusList\":[],\"alarmSeverityList\":[],\"alarmTypeList\":[],\"searchPropagatedAlarms\":false}"
"defaultConfig": "{\"timewindow\":{\"realtime\":{\"interval\":1000,\"timewindowMs\":86400000},\"aggregation\":{\"type\":\"NONE\",\"limit\":200}},\"showTitle\":true,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"4px\",\"settings\":{\"enableSelection\":true,\"enableSearch\":true,\"displayDetails\":true,\"allowAcknowledgment\":true,\"allowClear\":true,\"allowAssign\":true,\"displayActivity\":true,\"displayPagination\":true,\"defaultPageSize\":10,\"defaultSortOrder\":\"-createdTime\",\"enableSelectColumnDisplay\":true,\"enableStickyAction\":false,\"enableFilter\":true},\"title\":\"Alarms table\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400,\"padding\":\"5px 10px 5px 10px\"},\"useDashboardTimewindow\":false,\"showLegend\":false,\"alarmSource\":{\"type\":\"function\",\"dataKeys\":[{\"name\":\"createdTime\",\"type\":\"alarm\",\"label\":\"Created time\",\"color\":\"#2196f3\",\"settings\":{\"useCellStyleFunction\":false,\"cellStyleFunction\":\"\",\"useCellContentFunction\":false,\"cellContentFunction\":\"\"},\"_hash\":0.021092237451093787},{\"name\":\"originator\",\"type\":\"alarm\",\"label\":\"Originator\",\"color\":\"#4caf50\",\"settings\":{\"useCellStyleFunction\":false,\"cellStyleFunction\":\"\",\"useCellContentFunction\":false,\"cellContentFunction\":\"\"},\"_hash\":0.2780007688856758},{\"name\":\"type\",\"type\":\"alarm\",\"label\":\"Type\",\"color\":\"#f44336\",\"settings\":{\"useCellStyleFunction\":false,\"cellStyleFunction\":\"\",\"useCellContentFunction\":false,\"cellContentFunction\":\"\"},\"_hash\":0.7323586880398418},{\"name\":\"severity\",\"type\":\"alarm\",\"label\":\"Severity\",\"color\":\"#ffc107\",\"settings\":{\"useCellStyleFunction\":false,\"useCellContentFunction\":false},\"_hash\":0.09927019860088193},{\"name\":\"status\",\"type\":\"alarm\",\"label\":\"Status\",\"color\":\"#607d8b\",\"settings\":{\"useCellStyleFunction\":false,\"cellStyleFunction\":\"\",\"useCellContentFunction\":false,\"cellContentFunction\":\"\"},\"_hash\":0.6588418951443418},{\"name\":\"assignee\",\"type\":\"alarm\",\"label\":\"Assignee\",\"color\":\"#9c27b0\",\"settings\":{},\"_hash\":0.5008441077416634}],\"entityAliasId\":null,\"name\":\"alarms\"},\"alarmSearchStatus\":\"ANY\",\"alarmsPollingInterval\":5,\"showTitleIcon\":false,\"titleIcon\":\"more_horiz\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"displayTimewindow\":true,\"actions\":{},\"alarmStatusList\":[],\"alarmSeverityList\":[],\"alarmTypeList\":[],\"searchPropagatedAlarms\":false}"
}
}
]

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

@ -25,8 +25,7 @@
<mat-autocomplete class="tb-assignee-autocomplete"
#userAutocomplete="matAutocomplete"
[displayWith]="displayUserFn"
(optionSelected)="selected($event)"
panelWidth="260px">
(optionSelected)="selected($event)">
<mat-option [fxHide]="!assigneeId" [value]="null">
<mat-icon class="unassigned-icon">account_circle</mat-icon>
<span translate>alarm.unassigned</span>

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

@ -37,7 +37,6 @@
.tb-assignee-autocomplete {
&.tb-assignee-autocomplete.mat-mdc-autocomplete-panel {
position: relative;
left: -8px;
margin-top: 8px;
box-shadow: none !important;
}
@ -58,7 +57,6 @@
align-items: center;
margin-right: 8px;
border-radius: 50%;
background-color: #5cb445;
width: 28px;
height: 28px;
min-width: 28px;

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

@ -16,28 +16,14 @@
-->
<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)">
<div (click)="openAlarmAssigneePanel($event, alarm)">
<mat-form-field fxFlex class="mat-block">
<mat-label translate>alarm.assignee</mat-label>
<input matInput readonly [value]="getAssignee()">
<span *ngIf="alarm?.assigneeId" matPrefix 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>
<mat-icon *ngIf="!alarm?.assigneeId" matPrefix class="unassigned-icon">account_circle</mat-icon>
<mat-icon matSuffix>arrow_drop_down</mat-icon>
</mat-form-field>
</div>

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

@ -14,36 +14,29 @@
* limitations under the License.
*/
:host {
.tb-assignee {
cursor: pointer;
max-width: 273px;
.assigned-container {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
.user-avatar {
display: inline-flex;
justify-content: center;
align-items: center;
border-radius: 50%;
width: 28px;
height: 28px;
min-width: 28px;
min-height: 28px;
color: white;
font-size: 13px;
font-weight: 700;
margin-left: 12px;
margin-right: 20px;
}
.user-avatar {
display: inline-flex;
justify-content: center;
align-items: center;
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;
color: rgba(0, 0, 0, 0.38);
overflow: visible;
}
}
.unassigned-icon {
width: 28px;
height: 28px;
font-size: 28px;
color: rgba(0, 0, 0, 0.38);
overflow: visible;
margin-left: 12px;
margin-right: 20px;
padding: 0;
}

92
ui-ngx/src/app/modules/home/components/alarm/alarm-assignee.component.ts

@ -14,17 +14,17 @@
/// limitations under the License.
///
import {
Component, EventEmitter, Injector, Input, Output, StaticProvider, ViewContainerRef
} from '@angular/core';
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,
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';
import { TranslateService } from '@ngx-translate/core';
@Component({
selector: 'tb-alarm-assignee',
@ -35,12 +35,26 @@ export class AlarmAssigneeComponent {
@Input()
alarm: AlarmInfo;
@Input()
allowAssign: boolean;
@Output()
alarmReassigned = new EventEmitter<boolean>();
constructor(private utilsService: UtilsService,
private overlay: Overlay,
private viewContainerRef: ViewContainerRef) {
private viewContainerRef: ViewContainerRef,
private translateService: TranslateService) {
}
getAssignee() {
if (this.alarm) {
if (this.alarm.assignee) {
return this.getUserDisplayName(this.alarm.assignee);
} else {
return this.translateService.instant('alarm.unassigned');
}
}
}
getUserDisplayName(entity: AlarmAssignee) {
@ -103,39 +117,41 @@ export class AlarmAssigneeComponent {
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));
if (this.allowAssign) {
const target = $event.currentTarget;
const config = new OverlayConfig();
config.backdropClass = 'cdk-overlay-transparent-backdrop';
config.hasBackdrop = true;
const connectedPosition: ConnectedPosition = {
originX: 'center',
originY: 'center',
overlayX: 'center',
overlayY: 'top'
};
config.positionStrategy = this.overlay.position().flexibleConnectedTo(target as HTMLElement)
.withPositions([connectedPosition]);
config.width = (target as HTMLElement).offsetWidth;
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));
}
}
}

17
ui-ngx/src/app/modules/home/components/alarm/alarm-comment-dialog.component.html

@ -17,11 +17,8 @@
-->
<div style="min-width: 600px;">
<mat-toolbar color="primary">
<h2>{{ 'alarm.comments' | translate }}</h2>
<span fxFlex></span>
<button mat-icon-button
(click)="close()"
type="button">
<button mat-icon-button (click)="close()" type="button">
<mat-icon class="material-icons">close</mat-icon>
</button>
</mat-toolbar>
@ -29,16 +26,8 @@
</mat-progress-bar>
<div style="height: 4px;" *ngIf="!(isLoading$ | async)"></div>
<div mat-dialog-content style="padding: 0">
<tb-alarm-comment #alarmCommentComponent [alarmId]="alarmId"
[commentsHeaderEnabled]="commentsHeaderEnabled">
<tb-alarm-comment [alarmId]="alarmId"
[alarmActivityOnly]="true">
</tb-alarm-comment>
</div>
<div mat-dialog-actions fxLayout="row" fxLayoutAlign="end">
<button mat-raised-button color="primary"
type="button"
[disabled]="(isLoading$ | async)"
(click)="close()" cdkFocusInitial>
{{ 'action.close' | translate }}
</button>
</div>
</div>

4
ui-ngx/src/app/modules/home/components/alarm/alarm-comment-dialog.component.ts

@ -25,7 +25,6 @@ import { AlarmInfo } from '@shared/models/alarm.models';
export interface AlarmCommentDialogData {
alarmId?: string;
alarm?: AlarmInfo;
commentsHeaderEnabled: boolean;
}
@Component({
@ -37,14 +36,11 @@ export class AlarmCommentDialogComponent extends DialogComponent<AlarmCommentDia
alarmId: string;
commentsHeaderEnabled: boolean = false;
constructor(protected store: Store<AppState>,
protected router: Router,
@Inject(MAT_DIALOG_DATA) public data: AlarmCommentDialogData,
public dialogRef: MatDialogRef<AlarmCommentDialogComponent, void>) {
super(store, router, dialogRef);
this.commentsHeaderEnabled = this.data.commentsHeaderEnabled
this.alarmId = this.data.alarmId;
}

241
ui-ngx/src/app/modules/home/components/alarm/alarm-comment.component.html

@ -17,129 +17,141 @@
-->
<div [formGroup]="alarmCommentFormGroup" class="tb-alarm-comments" fxLayout="column">
<div class="tb-alarm-comments-header" fxLayout="row" fxLayoutAlign="space-between baseline">
<span *ngIf="commentsHeaderEnabled" class="tb-alarm-comments-header-title">
{{ 'alarm-comment.comments' | translate }}
</span>
<div style="margin-left: auto">
<button mat-icon-button
type="button"
(click)="changeSortDirection()"
matTooltip="{{ 'alarm-comment.sort-direction' | translate }}"
matTooltipPosition="above">
<mat-icon class="material-icons">{{ getSortDirectionIcon() }}</mat-icon>
</button>
<button mat-icon-button
type="button"
(click)="loadAlarmComments()"
matTooltip="{{ 'alarm-comment.refresh' | translate }}"
matTooltipPosition="above">
<mat-icon class="material-icons">refresh</mat-icon>
</button>
<div class="header" [ngClass]="{'activity-only': alarmActivityOnly}">
<div class="header-container" fxLayout="row" fxLayoutAlign="space-between center"
[ngClass]="{'asc': isDirectionAscending(), 'activity-only': alarmActivityOnly}">
<span class="header-title" translate>
alarm-activity.activity
</span>
<div style="margin-left: auto">
<button mat-icon-button
type="button"
(click)="changeSortDirection()"
[matTooltip]="getSortDirectionTooltipText()"
matTooltipPosition="above">
<mat-icon class="material-icons" [svgIcon]="getSortDirectionIcon()"></mat-icon>
</button>
<button mat-icon-button
type="button"
(click)="loadAlarmComments()"
matTooltip="{{ 'alarm-activity.refresh' | translate }}"
matTooltipPosition="above">
<mat-icon class="material-icons">refresh</mat-icon>
</button>
</div>
</div>
</div>
<div fxLayout="column" fxLayoutGap="32px">
<ng-container *ngIf="isDirectionDescending()">
<div fxLayout="column">
<ng-container *ngIf="!isDirectionAscending()">
<ng-container *ngTemplateOutlet="commentInput"></ng-container>
</ng-container>
<div fxFlex *ngFor="let displayDataElement of displayData; index as i">
<div style="margin-left: 38px"
*ngIf="displayDataElement.isSystemComment; else userComment">
<span class="tb-alarm-comments-system-text"
style="margin-right: 8px">
{{ displayDataElement.commentText }}
</span>
<span class="tb-alarm-comments-time">
{{ displayDataElement.createdDateAgo }}
</span>
</div>
<ng-template #userComment>
<div fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="8px"
*ngIf="!displayDataElement.edit; else commentEditing"
(mouseenter)="onCommentMouseEnter(displayDataElement.commentId, i)"
(mouseleave)="onCommentMouseLeave(i)">
<div fxLayout="row" fxLayoutAlign="center center" fxFlexAlign="start"
class="tb-alarm-comments-user-avatar"
[style.background-color]="displayDataElement.avatarBgColor">
{{ getUserInitials(displayDataElement.displayName) }}
</div>
<div fxFlex fxLayout="column" fxLayoutGap="5px">
<div fxLayout="row" fxLayoutGap="8px" fxLayoutAlign="start center">
<span class="tb-alarm-comments-user-name">{{ displayDataElement.displayName }}</span>
<span class="tb-alarm-comments-time" *ngIf="displayDataElement.isEdited">
edited {{ displayDataElement.editedDateAgo }}
</span>
<span class="tb-alarm-comments-time" *ngIf="!displayDataElement.isEdited">
{{ displayDataElement.createdDateAgo }}
</span>
</div>
<span class="tb-alarm-comments-text">{{ displayDataElement.commentText }}</span>
</div>
<div fxLayout="row" class="tb-alarm-comments-action-buttons"
[ngClass]="{ 'show-buttons': displayDataElement.showActions }">
<button mat-icon-button
type="button"
(click)="editComment(displayDataElement.commentId)">
<mat-icon class="material-icons">edit</mat-icon>
</button>
<button mat-icon-button
type="button"
(click)="deleteComment(displayDataElement.commentId)">
<mat-icon class="material-icons">delete</mat-icon>
</button>
</div>
<div class="comments-container" fxLayout="column" fxLayoutGap="12px" style="padding: 24px"
[ngClass]="{'asc': isDirectionAscending(), 'activity-only': alarmActivityOnly}">
<div fxFlex *ngFor="let displayDataElement of displayData; index as i">
<div *ngIf="displayDataElement.isSystemComment; else userComment" style="margin-left: 38px;">
<span class="system-text" style="margin-right: 8px">
{{ displayDataElement.commentText }}
</span>
<span class="time" style="padding: 3px"
matTooltip="{{ displayDataElement.createdTime | date:'yyyy-MM-dd HH:mm:ss' }}"
matTooltipPosition="right">
{{ displayDataElement.createdDateAgo }}
</span>
</div>
<ng-template #commentEditing>
<div fxLayoutAlign="row center" fxLayoutGap="8px">
<div fxLayout="row" fxLayoutAlign="center center"
class="tb-alarm-comments-user-avatar"
<ng-template #userComment>
<div class="user-comment" fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="8px"
*ngIf="!displayDataElement.edit; else commentEditing"
(mouseenter)="onCommentMouseEnter(displayDataElement.commentId, i)"
(mouseleave)="onCommentMouseLeave(i)">
<div fxLayout="row" fxLayoutAlign="center center" fxFlexAlign="start"
class="user-avatar"
[style.background-color]="displayDataElement.avatarBgColor">
{{ getUserInitials(displayDataElement.displayName) }}
</div>
<mat-form-field fxFlex appearance="fill" class="mat-block">
<textarea matInput
type="text"
placeholder="{{ 'alarm-comment.add' | translate }}"
cdkTextareaAutosize
cdkAutosizeMinRows="1"
cols="1"
formControlName="alarmCommentEdit"
(keyup.enter)="saveEditedComment(displayDataElement.commentId)"
(keydown.enter)="$event.preventDefault()">
</textarea>
<div matSuffix fxLayout="row">
<button mat-icon-button
(click)="cancelEdit(displayDataElement.commentId)"
type="button">
<mat-icon class="material-icons red-button">close</mat-icon>
</button>
<button mat-icon-button
type="button"
(click)="saveEditedComment(displayDataElement.commentId)">
<mat-icon class="material-icons green-button">check</mat-icon>
</button>
<div fxFlex fxLayout="column" fxLayoutGap="5px">
<div fxLayout="row" fxLayoutGap="8px" fxLayoutAlign="start center">
<span class="user-name">{{ displayDataElement.displayName }}</span>
<span class="time"
matTooltip="{{ displayDataElement.createdTime | date:'yyyy-MM-dd HH:mm:ss' }}"
matTooltipPosition="right">
{{ displayDataElement.createdDateAgo }}
</span>
<span class="time" *ngIf="displayDataElement.isEdited"
matTooltip="{{ displayDataElement.editedDateAgo }} {{ displayDataElement.editedTime | date:'yyyy-MM-dd HH:mm:ss' }}"
matTooltipPosition="right">
Edited
</span>
</div>
</mat-form-field>
<span class="text">{{ displayDataElement.commentText }}</span>
</div>
<div fxLayout="row" class="action-buttons"
[ngClass]="{ 'show-buttons': displayDataElement.showActions }">
<button mat-icon-button
type="button"
(click)="editComment(displayDataElement.commentId)">
<mat-icon class="material-icons">edit</mat-icon>
</button>
<button mat-icon-button
type="button"
(click)="deleteComment(displayDataElement.commentId)">
<mat-icon class="material-icons">delete</mat-icon>
</button>
</div>
</div>
<ng-template #commentEditing>
<div fxLayoutAlign="row center" fxLayoutGap="8px">
<div fxLayout="row" fxLayoutAlign="center center"
class="user-avatar"
[style.background-color]="displayDataElement.avatarBgColor">
{{ getUserInitials(displayDataElement.displayName) }}
</div>
<mat-form-field fxFlex class="mat-block tb-appearance-transparent">
<textarea matInput
type="text"
placeholder="{{ 'alarm-activity.add' | translate }}"
cdkTextareaAutosize
cdkAutosizeMinRows="1"
cols="1"
formControlName="alarmCommentEdit"
(keyup.enter)="saveEditedComment(displayDataElement.commentId)"
(keydown.enter)="$event.preventDefault()">
</textarea>
<div matSuffix fxLayout="row">
<button mat-icon-button
(click)="cancelEdit(displayDataElement.commentId)"
type="button">
<mat-icon class="material-icons red-button">close</mat-icon>
</button>
<button mat-icon-button
type="button"
(click)="saveEditedComment(displayDataElement.commentId)">
<mat-icon class="material-icons green-button">check</mat-icon>
</button>
</div>
</mat-form-field>
</div>
</ng-template>
</ng-template>
</ng-template>
</div>
</div>
<ng-container *ngIf="isDirectionAscending()">
<ng-container *ngTemplateOutlet="commentInput"></ng-container>
</ng-container>
</div>
<ng-template #commentInput>
<div fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="8px" fxFlex>
<div fxLayout="row" fxLayoutAlign="center center"
class="tb-alarm-comments-user-avatar"
*ngIf="userDisplayName$ | async; let userDisplayName"
[style.background-color]="getCurrentUserBgColor(userDisplayName)">
{{ getUserInitials(userDisplayName) }}
</div>
<mat-form-field appearance="fill" fxFlex class="mat-block">
<div style="background-color: white" class="comment-input"
[ngClass]="{'newest-first': !isDirectionAscending(), 'oldest-first': isDirectionAscending(), 'activity-only': alarmActivityOnly}">
<div fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="8px" class="inner-wrap">
<div fxLayout="row" fxLayoutAlign="center center"
class="user-avatar"
*ngIf="userDisplayName$ | async; let userDisplayName"
[style.background-color]="getCurrentUserBgColor(userDisplayName)">
{{ getUserInitials(userDisplayName) }}
</div>
<mat-form-field fxFlex class="mat-block tb-appearance-transparent">
<textarea matInput
type="text"
placeholder="{{ 'alarm-comment.add' | translate }}"
placeholder="{{ 'alarm-activity.add' | translate }}"
cdkTextareaAutosize
cdkAutosizeMinRows="1"
cols="1"
@ -147,16 +159,17 @@
(keyup.enter)="saveComment()"
(keydown.enter)="$event.preventDefault()">
</textarea>
<button mat-icon-button
type="button"
matSuffix
*ngIf="getAlarmCommentFormControl().value"
(click)="saveComment()">
<mat-icon color="primary">
send
</mat-icon>
</button>
</mat-form-field>
<button mat-icon-button
type="button"
matSuffix
*ngIf="getAlarmCommentFormControl().value"
(click)="saveComment()">
<mat-icon color="primary">
send
</mat-icon>
</button>
</mat-form-field>
</div>
</div>
</ng-template>
</div>

150
ui-ngx/src/app/modules/home/components/alarm/alarm-comment.component.scss

@ -13,69 +13,175 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
@use '@angular/material' as mat;
@import '../theme.scss';
$main-color: rgba(mat.get-color-from-palette($tb-primary, 50), 0.4);
$border: 1px solid mat.get-color-from-palette($tb-primary);
:host {
.tb-alarm-comments {
padding: 16px 24px 24px 24px;
background-color: #fafafa;
background-color: $main-color;
max-width: 600px;
width: 100%;
&-header {
background-color: #fafafa;
.comment-input {
position: sticky;
top: -25px;
z-index: 1;
margin-bottom: 10px;
&-title {
color: rgba(0, 0, 0, 0.76);
letter-spacing: 0.25px;
font-weight: 500;
.inner-wrap {
border: $border;
padding: 0 24px;
background-color: $main-color;
}
.mat-icon {
color: rgba(0, 0, 0, 0.38);
&.oldest-first {
bottom: -24px;
box-shadow: 0px -4px 12px rgba(0, 0, 0, 0.04);
.inner-wrap {
border-radius: 0 0 8px 8px;
}
&.activity-only {
bottom: -1px;
.inner-wrap {
border: none
}
}
}
&.newest-first {
top: 24px;
box-shadow: 0 8px 10px rgba(23, 33, 90, 0.08);
.inner-wrap {
border-top: none;
}
&.activity-only {
top: 48px;
box-shadow: 0 8px 10px rgba(23, 33, 90, 0.08);
.inner-wrap {
border: none;
}
}
}
}
&-user-avatar {
.header {
z-index: 1;
position: sticky;
top: -24px;
background-color: white;
&.activity-only {
top: 0;
}
&-container {
padding: 0 24px;
background-color: $main-color;
border: $border;
border-bottom: none;
border-radius: 8px 8px 0px 0px;
&.activity-only {
border: none;
}
&.asc {
border-bottom: $border;
box-shadow: 0px 4px 10px rgba(23, 33, 90, 0.08);
&.activity-only {
border: none;
}
}
.header-title {
color: rgba(0, 0, 0, 0.76);
letter-spacing: 0.25px;
font-weight: 500;
}
.mat-icon {
color: rgba(0, 0, 0, 0.38);
}
}
}
.comments-container {
border: $border;
border-top: none;
border-radius: 0px 0px 8px 8px;
&.activity-only {
border: none;
}
&.asc {
border-bottom: none;
border-radius: unset;
}
}
.user-comment {
padding: 4px;
border-radius: 8px;
&:hover {
background-color: $main-color;
}
}
.user-avatar {
width: 28px;
min-width: 28px;
height: 28px;
min-height: 28px;
border-radius: 50%;
font-weight: 700;
color: #FFFFFF;
color: white;
font-size: 13px;
}
&-user-name {
.user-name {
font-size: 16px;
color: rgba(0, 0, 0, 0.76);
font-weight: 500;
letter-spacing: 0.25px;
}
&-time {
.time {
font-size: 14px;
font-weight: 400;
color: rgba(0, 0, 0, 0.38);
letter-spacing: 0.2px
letter-spacing: 0.2px;
border-radius: 16px;
padding: 0 3px;
&:hover {
background: rgba(0, 0, 0, 0.04);
}
}
&-system-text {
color: rgba(0, 0, 0, 0.38);
.system-text {
font-weight: 500;
font-size: 14px;
letter-spacing: 0.25px;
color: rgba(0, 0, 0, 0.38);
}
&-text {
.text {
white-space: pre-line;
word-break: break-word;
color: rgba(0, 0, 0, 0.54);
font-size: 16px;
letter-spacing: 0.15px;
color: rgba(0, 0, 0, 0.54);
}
&-action-buttons {
.action-buttons {
visibility: hidden;
.mat-icon {
color: rgba(0, 0, 0, 0.38);

25
ui-ngx/src/app/modules/home/components/alarm/alarm-comment.component.ts

@ -34,9 +34,11 @@ import { EntityType } from '@shared/models/entity-type.models';
interface AlarmCommentsDisplayData {
commentId?: string,
displayName?: string,
createdTime: number,
createdDateAgo?: string,
edit?: boolean,
isEdited?: boolean,
editedTime?: number;
editedDateAgo?: string,
showActions?: boolean,
commentText?: string,
@ -54,7 +56,7 @@ export class AlarmCommentComponent implements OnInit {
alarmId: string;
@Input()
commentsHeaderEnabled: boolean = true;
alarmActivityOnly: boolean = false;
authUser: AuthUser;
@ -110,7 +112,8 @@ export class AlarmCommentComponent implements OnInit {
this.alarmComments = pagedData.data;
this.displayData.length = 0;
for (let alarmComment of pagedData.data) {
let displayDataElement: AlarmCommentsDisplayData = {};
let displayDataElement = {} as AlarmCommentsDisplayData;
displayDataElement.createdTime = alarmComment.createdTime;
displayDataElement.createdDateAgo = this.dateAgoPipe.transform(alarmComment.createdTime);
displayDataElement.commentText = alarmComment.comment.text;
displayDataElement.isSystemComment = alarmComment.type === AlarmCommentType.SYSTEM;
@ -119,7 +122,9 @@ export class AlarmCommentComponent implements OnInit {
displayDataElement.displayName = this.getUserDisplayName(alarmComment);
displayDataElement.edit = false;
displayDataElement.isEdited = alarmComment.comment.edited;
displayDataElement.editedDateAgo = this.dateAgoPipe.transform(alarmComment.comment.editedOn).toLowerCase();
displayDataElement.editedTime = alarmComment.comment.editedOn;
displayDataElement.editedDateAgo = this.dateAgoPipe.transform(alarmComment.comment.editedOn) +
' ' + this.translate.instant('alarm-activity.ago') + '\n';
displayDataElement.showActions = false;
displayDataElement.isSystemComment = false;
displayDataElement.avatarBgColor = this.utilsService.stringToHslColor(displayDataElement.displayName,
@ -194,7 +199,7 @@ export class AlarmCommentComponent implements OnInit {
const alarmCommentInfo: AlarmComment = this.getAlarmCommentById(commentId);
const commentText: string = alarmCommentInfo.comment.text;
this.dialogService.confirm(
this.translate.instant('alarm-comment.delete-alarm-comment'),
this.translate.instant('alarm-activity.delete-alarm-comment'),
commentText,
this.translate.instant('action.cancel'),
this.translate.instant('action.delete')).subscribe(
@ -211,15 +216,17 @@ export class AlarmCommentComponent implements OnInit {
}
getSortDirectionIcon() {
return this.alarmCommentSortOrder.direction === Direction.DESC ? 'arrow_downward' : 'arrow_upward'
return this.alarmCommentSortOrder.direction === Direction.DESC ? 'mdi:sort-descending' : 'mdi:sort-ascending'
}
isDirectionAscending() {
return this.alarmCommentSortOrder.direction === Direction.ASC;
getSortDirectionTooltipText() {
let text = this.alarmCommentSortOrder.direction === Direction.DESC ? 'alarm-activity.newest-first' :
'alarm-activity.oldest-first';
return this.translate.instant(text);
}
isDirectionDescending() {
return this.alarmCommentSortOrder.direction === Direction.DESC;
isDirectionAscending() {
return this.alarmCommentSortOrder.direction === Direction.ASC;
}
onCommentMouseEnter(commentId: string, displayDataIndex: number): void {

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

@ -28,90 +28,64 @@
<mat-progress-bar color="warn" mode="indeterminate" *ngIf="isLoading$ | async">
</mat-progress-bar>
<div style="height: 4px;" *ngIf="!(isLoading$ | async)"></div>
<div mat-dialog-content style="padding: 24px 0 0 0">
<fieldset [disabled]="isLoading$ | async" style="padding: 0 24px">
<div mat-dialog-content>
<fieldset [disabled]="isLoading$ | async" style="margin-bottom: 22px">
<div fxLayout="row" fxLayoutGap="6px">
<mat-form-field fxFlex class="mat-block">
<mat-label translate>alarm.originator</mat-label>
<input matInput formControlName="originatorName" readonly>
</mat-form-field>
<mat-form-field fxFlex class="mat-block">
<mat-label translate>alarm.created-time</mat-label>
<input matInput formControlName="createdTime" readonly>
<mat-label translate>alarm.severity</mat-label>
<input matInput formControlName="alarmSeverity" readonly
[ngStyle]="{fontWeight: 'bold', color: alarmSeverityColorsMap.get((alarm$ | async)?.severity)}">
</mat-form-field>
</div>
<div fxLayout="row" fxLayoutGap="6px">
<mat-form-field fxFlex class="mat-block">
<mat-label translate>alarm.type</mat-label>
<input matInput formControlName="type" readonly>
<mat-form-field *ngIf="alarmFormGroup.get('startTime').value" fxFlex class="mat-block">
<mat-label translate>alarm.start-time</mat-label>
<input matInput formControlName="startTime" readonly>
</mat-form-field>
<mat-form-field fxFlex class="mat-block">
<mat-label translate>alarm.severity</mat-label>
<input matInput formControlName="alarmSeverity" readonly
[ngStyle]="{fontWeight: 'bold', color: alarmSeverityColorsMap.get((alarm$ | async)?.severity)}">
<mat-form-field *ngIf="alarmFormGroup.get('duration').value" fxFlex class="mat-block">
<mat-label translate>alarm.duration</mat-label>
<input matInput formControlName="duration" readonly>
</mat-form-field>
</div>
<div fxLayout="row" fxLayoutGap="6px">
<mat-form-field fxFlex class="mat-block">
<mat-label translate>alarm.type</mat-label>
<input matInput formControlName="type" readonly>
</mat-form-field>
<mat-form-field fxFlex class="mat-block">
<mat-label translate>alarm.status</mat-label>
<input matInput formControlName="alarmStatus" 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">
<mat-panel-title>
</mat-panel-title>
<mat-panel-description fxLayoutAlign="end center" fxHide.xs translate>
alarm.advanced-info
alarm.show-more
</mat-panel-description>
</mat-expansion-panel-header>
<ng-template matExpansionPanelContent>
<div fxLayout="row" fxLayoutGap="6px" *ngIf="alarmFormGroup.get('startTime').value ||
alarmFormGroup.get('endTime').value">
<mat-form-field *ngIf="alarmFormGroup.get('startTime').value" fxFlex class="mat-block">
<mat-label translate>alarm.start-time</mat-label>
<input matInput formControlName="startTime" readonly>
</mat-form-field>
<mat-form-field *ngIf="alarmFormGroup.get('endTime').value" fxFlex class="mat-block">
<mat-label translate>alarm.end-time</mat-label>
<input matInput formControlName="endTime" readonly>
</mat-form-field>
<span fxFlex *ngIf="!alarmFormGroup.get('startTime').value || !alarmFormGroup.get('endTime').value"></span>
</div>
<div fxLayout="row" fxLayoutGap="6px" *ngIf="alarmFormGroup.get('ackTime').value ||
alarmFormGroup.get('clearTime').value">
<mat-form-field *ngIf="alarmFormGroup.get('ackTime').value" fxFlex class="mat-block">
<mat-label translate>alarm.ack-time</mat-label>
<input matInput formControlName="ackTime" readonly>
</mat-form-field>
<mat-form-field *ngIf="alarmFormGroup.get('clearTime').value" fxFlex class="mat-block">
<mat-label translate>alarm.clear-time</mat-label>
<input matInput formControlName="clearTime" readonly>
</mat-form-field>
<span fxFlex *ngIf="!alarmFormGroup.get('ackTime').value || !alarmFormGroup.get('clearTime').value"></span>
</div>
<tb-json-object-edit
*ngIf="displayDetails"
formControlName="alarmDetails"
readonly
label="{{ 'alarm.details' | translate }}">
label="{{ 'alarm.additional-info' | translate }}">
</tb-json-object-edit>
</ng-template>
</mat-expansion-panel>
</fieldset>
<tb-alarm-assignee fxFlex [allowAssign]="allowAssign"
[alarm]="alarm$ | async"
(alarmReassigned)="onReassign()">
</tb-alarm-assignee>
<tb-alarm-comment #alarmCommentComponent [alarmId]="alarmId"></tb-alarm-comment>
</div>
<div mat-dialog-actions fxLayout="row">
<button mat-button color="primary"
type="button"
[disabled]="(isLoading$ | async)"
(click)="close()" cdkFocusInitial>
{{ 'action.close' | translate }}
</button>
<span fxFlex></span>
<div fxLayout="row" *ngIf="alarm$ | async; let alarm;" fxLayoutGap="8px">
<button *ngIf="allowAcknowledgment && (alarm.status === alarmStatuses.ACTIVE_UNACK ||

40
ui-ngx/src/app/modules/home/components/alarm/alarm-details-dialog.component.ts

@ -34,6 +34,7 @@ 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 { MillisecondsToTimeStringPipe } from '@shared/pipe/milliseconds-to-time-string.pipe';
export interface AlarmDetailsDialogData {
alarmId?: string;
@ -41,6 +42,7 @@ export interface AlarmDetailsDialogData {
allowAcknowledgment: boolean;
allowClear: boolean;
displayDetails: boolean;
allowAssign: boolean;
}
@Component({
@ -56,6 +58,7 @@ export class AlarmDetailsDialogComponent extends DialogComponent<AlarmDetailsDia
allowAcknowledgment: boolean;
allowClear: boolean;
displayDetails: boolean;
allowAssign: boolean;
loadAlarmSubject = new ReplaySubject<AlarmInfo>();
alarm$: Observable<AlarmInfo> = this.loadAlarmSubject.asObservable().pipe(
@ -72,6 +75,7 @@ export class AlarmDetailsDialogComponent extends DialogComponent<AlarmDetailsDia
constructor(protected store: Store<AppState>,
protected router: Router,
private datePipe: DatePipe,
private millisecondsToTimeStringPipe: MillisecondsToTimeStringPipe,
private translate: TranslateService,
@Inject(MAT_DIALOG_DATA) public data: AlarmDetailsDialogData,
private alarmService: AlarmService,
@ -82,17 +86,15 @@ export class AlarmDetailsDialogComponent extends DialogComponent<AlarmDetailsDia
this.allowAcknowledgment = data.allowAcknowledgment;
this.allowClear = data.allowClear;
this.displayDetails = data.displayDetails;
this.allowAssign = data.allowAssign;
this.alarmFormGroup = this.fb.group(
{
createdTime: [''],
originatorName: [''],
alarmSeverity: [''],
startTime: [''],
endTime: [''],
ackTime: [''],
clearTime: [''],
duration: [''],
type: [''],
alarmSeverity: [''],
alarmStatus: [''],
alarmDetails: [null]
}
@ -116,29 +118,25 @@ export class AlarmDetailsDialogComponent extends DialogComponent<AlarmDetailsDia
}
loadAlarmFields(alarm: AlarmInfo) {
this.alarmFormGroup.get('createdTime')
.patchValue(this.datePipe.transform(alarm.createdTime, 'yyyy-MM-dd HH:mm:ss'));
this.alarmFormGroup.get('originatorName')
.patchValue(alarm.originatorName);
.patchValue(alarm.originatorLabel ? alarm.originatorLabel : alarm.originatorName);
this.alarmFormGroup.get('alarmSeverity')
.patchValue(this.translate.instant(alarmSeverityTranslations.get(alarm.severity)));
if (alarm.startTs) {
this.alarmFormGroup.get('startTime')
.patchValue(this.datePipe.transform(alarm.startTs, 'yyyy-MM-dd HH:mm:ss'));
}
if (alarm.endTs) {
this.alarmFormGroup.get('endTime')
.patchValue(this.datePipe.transform(alarm.endTs, 'yyyy-MM-dd HH:mm:ss'));
}
if (alarm.ackTs) {
this.alarmFormGroup.get('ackTime')
.patchValue(this.datePipe.transform(alarm.ackTs, 'yyyy-MM-dd HH:mm:ss'));
}
if (alarm.clearTs) {
this.alarmFormGroup.get('clearTime')
.patchValue(this.datePipe.transform(alarm.clearTs, 'yyyy-MM-dd HH:mm:ss'));
if (alarm.startTs || alarm.endTs) {
let duration = '';
if (alarm.startTs && (alarm.status === AlarmStatus.ACTIVE_ACK || alarm.status === AlarmStatus.ACTIVE_UNACK)) {
duration = this.millisecondsToTimeStringPipe.transform(Date.now() - alarm.startTs);
}
if (alarm.endTs && (alarm.status === AlarmStatus.CLEARED_ACK || alarm.status === AlarmStatus.CLEARED_UNACK)) {
duration = this.millisecondsToTimeStringPipe.transform(alarm.endTs - alarm.startTs);
}
this.alarmFormGroup.get('duration').patchValue(duration);
}
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)));
this.alarmFormGroup.get('alarmDetails').patchValue(alarm.details);

5
ui-ngx/src/app/modules/home/components/alarm/alarm-table-config.ts

@ -168,7 +168,7 @@ export class AlarmTableConfig extends EntityTableConfig<AlarmInfo, TimePageLink>
}
showAlarmDetails(entity: AlarmInfo) {
const isPermissionWrite = this.authUser.authority !== Authority.CUSTOMER_USER || entity.customerId.id === this.authUser.customerId;
const isPermissionWrite = this.authUser.authority !== Authority.CUSTOMER_USER || entity.customerId?.id === this.authUser.customerId;
this.dialog.open<AlarmDetailsDialogComponent, AlarmDetailsDialogData, boolean>
(AlarmDetailsDialogComponent,
{
@ -179,7 +179,8 @@ export class AlarmTableConfig extends EntityTableConfig<AlarmInfo, TimePageLink>
alarm: entity,
allowAcknowledgment: isPermissionWrite,
allowClear: isPermissionWrite,
displayDetails: true
displayDetails: true,
allowAssign: true
}
}).afterClosed().subscribe(
(res) => {

66
ui-ngx/src/app/modules/home/components/alarm/alarm-table.component.scss

@ -14,42 +14,44 @@
* limitations under the License.
*/
:host ::ng-deep {
tb-entities-table.tb-details-mode {
.mat-drawer-container {
background-color: white;
.assignee-cell {
display: flex;
justify-content: flex-start;
align-items: center;
.assigned-container {
max-width: 180px;
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 {
tb-entities-table {
&.tb-details-mode {
.mat-drawer-container {
background-color: white;
}
}
.assignee-cell {
display: flex;
justify-content: flex-start;
align-items: center;
.assigned-container {
max-width: 180px;
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;
font-size: 28px;
margin-right: 8px;
color: rgba(0, 0, 0, 0.38);
overflow: visible;
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;
}
}
}
}

26
ui-ngx/src/app/modules/home/components/widget/lib/alarms-table-widget.component.ts

@ -143,7 +143,7 @@ interface AlarmsTableWidgetSettings extends TableWidgetSettings {
enableSelection: boolean;
enableStatusFilter?: boolean;
enableFilter: boolean;
displayComments: boolean;
displayActivity: boolean;
displayDetails: boolean;
allowAcknowledgment: boolean;
allowClear: boolean;
@ -154,7 +154,7 @@ interface AlarmWidgetActionDescriptor extends TableCellButtonActionDescriptor {
details?: boolean;
acknowledge?: boolean;
clear?: boolean;
comments?: boolean;
activity?: boolean;
}
@Component({
@ -197,7 +197,7 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit,
private alarmsTitlePattern: string;
private displayComments = false;
private displayActivity = false;
private displayDetails = true;
public allowAcknowledgment = true;
private allowClear = true;
@ -335,7 +335,7 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit,
private initializeConfig() {
this.ctx.widgetActions = [this.searchAction, this.alarmFilterAction, this.columnDisplayAction];
this.displayComments = isDefined(this.settings.displayComments) ? this.settings.displayComments : false;
this.displayActivity = isDefined(this.settings.displayActivity) ? this.settings.displayActivity : false;
this.displayDetails = isDefined(this.settings.displayDetails) ? this.settings.displayDetails : true;
this.allowAcknowledgment = isDefined(this.settings.allowAcknowledgment) ? this.settings.allowAcknowledgment : true;
this.allowClear = isDefined(this.settings.allowClear) ? this.settings.allowClear : true;
@ -464,12 +464,12 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit,
this.sortOrderProperty = sortColumn ? sortColumn.def : null;
const actionCellDescriptors: AlarmWidgetActionDescriptor[] = [];
if (this.displayComments) {
if (this.displayActivity) {
actionCellDescriptors.push(
{
displayName: this.translate.instant('alarm-comment.comments'),
displayName: this.translate.instant('alarm-activity.activity'),
icon: 'comment',
comments: true
activity: true
} as AlarmWidgetActionDescriptor
);
}
@ -821,8 +821,8 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit,
this.ackAlarm($event, alarm);
} else if (actionDescriptor.clear) {
this.clearAlarm($event, alarm);
} else if (actionDescriptor.comments) {
this.openAlarmComments($event, alarm);
} else if (actionDescriptor.activity) {
this.openAlarmActivity($event, alarm);
} else {
if ($event) {
$event.stopPropagation();
@ -864,7 +864,8 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit,
alarmId: alarm.id.id,
allowAcknowledgment: this.allowAcknowledgment,
allowClear: this.allowClear,
displayDetails: true
displayDetails: true,
allowAssign: this.allowAssign
}
}).afterClosed().subscribe(
(res) => {
@ -988,7 +989,7 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit,
}
}
private openAlarmComments($event: Event, alarm: AlarmDataInfo) {
private openAlarmActivity($event: Event, alarm: AlarmDataInfo) {
if ($event) {
$event.stopPropagation();
}
@ -999,8 +1000,7 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit,
disableClose: true,
panelClass: ['tb-dialog', 'tb-fullscreen-dialog'],
data: {
alarmId: alarm.id.id,
commentsHeaderEnabled: false
alarmId: alarm.id.id
}
}).afterClosed()
}

4
ui-ngx/src/app/modules/home/components/widget/lib/settings/alarm/alarms-table-widget-settings.component.html

@ -58,8 +58,8 @@
</mat-select>
</mat-form-field>
<section fxLayout="column" fxLayoutGap="8px">
<mat-slide-toggle formControlName="displayComments">
{{ 'widgets.table.display-alarm-comments' | translate }}
<mat-slide-toggle formControlName="displayActivity">
{{ 'widgets.table.display-alarm-activity' | translate }}
</mat-slide-toggle>
<mat-slide-toggle fxFlex formControlName="displayDetails">
{{ 'widgets.table.display-alarm-details' | translate }}

2
ui-ngx/src/app/modules/home/components/widget/lib/settings/alarm/alarms-table-widget-settings.component.ts

@ -74,7 +74,7 @@ export class AlarmsTableWidgetSettingsComponent extends WidgetSettingsComponent
allowAcknowledgment: [settings.allowAcknowledgment, []],
allowClear: [settings.allowClear, []],
allowAssign: [settings.allowAssign, []],
displayComments: [settings.displayComments, []],
displayActivity: [settings.displayActivity, []],
displayPagination: [settings.displayPagination, []],
defaultPageSize: [settings.defaultPageSize, [Validators.min(1)]],
defaultSortOrder: [settings.defaultSortOrder, []],

13
ui-ngx/src/assets/locale/locale.constant-en_US.json

@ -465,6 +465,7 @@
"end-time": "End time",
"ack-time": "Acknowledged time",
"clear-time": "Cleared time",
"duration": "Duration",
"alarm-severity-list": "Alarm severity list",
"any-severity": "Any severity",
"severity-critical": "Critical",
@ -500,15 +501,19 @@
"any-type": "Any type",
"search-propagated-alarms": "Search propagated alarms",
"comments": "Alarm comments",
"advanced-info": "Advanced info"
"show-more": "Show more"
},
"alarm-comment": {
"alarm-activity": {
"add": "Add a comment...",
"alarm-comment": "Alarm comment",
"comments": "Comments",
"delete-alarm-comment": "Do you want to delete this comment?",
"refresh": "Refresh",
"sort-direction": "Sort direction"
"oldest-first": "Oldest first",
"newest-first": "Newest first",
"ago": "ago",
"activity": "Activity",
"export": "Export to CSV"
},
"alias": {
"add": "Add alias",
@ -5031,7 +5036,7 @@
"display-alarm-details": "Display alarm details",
"allow-alarms-ack": "Allow alarms acknowledgment",
"allow-alarms-clear": "Allow alarms clear",
"display-alarm-comments": "Display alarm comments",
"display-alarm-activity": "Display alarm activity",
"allow-alarms-assign": "Allow alarms assignment"
},
"value-source": {

Loading…
Cancel
Save