Merge pull request #8285 from rusikv/alarm-details-redesign
Alarm details redesign
This commit is contained in:
commit
a8d0d871e1
@ -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}"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -17,7 +17,7 @@
|
||||
:host {
|
||||
width: 100%;
|
||||
overflow: auto;
|
||||
background: #fff;
|
||||
background: white;
|
||||
box-shadow: 0px 1px 2px rgba(0, 0, 0, 0.3), 0px 2px 6px 2px rgba(0, 0, 0, 0.15);
|
||||
border-radius: 4px;
|
||||
}
|
||||
@ -27,7 +27,7 @@
|
||||
padding: 8px;
|
||||
height: 340px;
|
||||
font-size: 14px;
|
||||
background-color: #fff;
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
.mat-form-field-appearance-outline .mdc-notched-outline__trailing{
|
||||
@ -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,17 +57,16 @@
|
||||
align-items: center;
|
||||
margin-right: 8px;
|
||||
border-radius: 50%;
|
||||
background-color: #5cb445;
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
min-width: 28px;
|
||||
min-height: 28px;
|
||||
color: #fff;
|
||||
color: white;
|
||||
font-size: 13px;
|
||||
font-weight: 700
|
||||
}
|
||||
.user-display-name {
|
||||
max-width: 180px;
|
||||
max-width: 80%;
|
||||
overflow: hidden;
|
||||
span {
|
||||
overflow: hidden;
|
||||
@ -79,8 +77,8 @@
|
||||
}
|
||||
}
|
||||
.mdc-list-item__primary-text {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: start;
|
||||
align-items: center;
|
||||
line-height: normal;
|
||||
}
|
||||
|
||||
@ -65,6 +65,8 @@ export class AlarmAssigneePanelComponent implements OnInit, AfterViewInit, OnDe
|
||||
|
||||
assigneeId?: string;
|
||||
|
||||
reassigned: boolean = false;
|
||||
|
||||
selectUserFormGroup: FormGroup;
|
||||
|
||||
@ViewChild('userInput', {static: true}) userInput: ElementRef;
|
||||
@ -130,12 +132,18 @@ export class AlarmAssigneePanelComponent implements OnInit, AfterViewInit, OnDe
|
||||
|
||||
assign(user: User): void {
|
||||
this.alarmService.assignAlarm(this.alarmId, user.id.id, {ignoreLoading: true}).subscribe(
|
||||
() => this.overlayRef.dispose());
|
||||
() => {
|
||||
this.reassigned = true;
|
||||
this.overlayRef.dispose()
|
||||
});
|
||||
}
|
||||
|
||||
unassign(): void {
|
||||
this.alarmService.unassignAlarm(this.alarmId, {ignoreLoading: true}).subscribe(
|
||||
() => this.overlayRef.dispose());
|
||||
() => {
|
||||
this.reassigned = true;
|
||||
this.overlayRef.dispose()
|
||||
});
|
||||
}
|
||||
|
||||
fetchUsers(searchText?: string): Observable<Array<UserEmailInfo>> {
|
||||
|
||||
@ -16,28 +16,15 @@
|
||||
|
||||
-->
|
||||
|
||||
<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)">
|
||||
<mat-form-field fxFlex class="mat-block" style="margin-bottom: 25px"
|
||||
(click)="openAlarmAssigneePanel($event, alarm)"
|
||||
subscriptSizing="dynamic">
|
||||
<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>
|
||||
</div>
|
||||
<mat-icon *ngIf="!alarm?.assigneeId" matPrefix class="unassigned-icon">account_circle</mat-icon>
|
||||
<mat-icon matSuffix>arrow_drop_down</mat-icon>
|
||||
</mat-form-field>
|
||||
|
||||
@ -14,16 +14,6 @@
|
||||
* 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;
|
||||
@ -36,14 +26,17 @@
|
||||
color: white;
|
||||
font-size: 13px;
|
||||
font-weight: 700;
|
||||
margin-left: 12px;
|
||||
margin-right: 20px;
|
||||
}
|
||||
}
|
||||
.material-icons.unassigned-icon {
|
||||
|
||||
.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;
|
||||
}
|
||||
|
||||
@ -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,19 +117,20 @@ export class AlarmAssigneeComponent {
|
||||
if ($event) {
|
||||
$event.stopPropagation();
|
||||
}
|
||||
const target = $event.target || $event.srcElement || $event.currentTarget;
|
||||
if (this.allowAssign) {
|
||||
const target = $event.currentTarget;
|
||||
const config = new OverlayConfig();
|
||||
config.backdropClass = 'cdk-overlay-transparent-backdrop';
|
||||
config.hasBackdrop = true;
|
||||
const connectedPosition: ConnectedPosition = {
|
||||
originX: 'end',
|
||||
originX: 'center',
|
||||
originY: 'bottom',
|
||||
overlayX: 'end',
|
||||
overlayX: 'center',
|
||||
overlayY: 'top'
|
||||
};
|
||||
config.positionStrategy = this.overlay.position().flexibleConnectedTo(target as HTMLElement)
|
||||
.withPositions([connectedPosition]);
|
||||
config.minWidth = '260px';
|
||||
config.width = (target as HTMLElement).offsetWidth;
|
||||
const overlayRef = this.overlay.create(config);
|
||||
overlayRef.backdropClick().subscribe(() => {
|
||||
overlayRef.dispose();
|
||||
@ -137,5 +152,6 @@ export class AlarmAssigneeComponent {
|
||||
overlayRef.attach(new ComponentPortal(AlarmAssigneePanelComponent,
|
||||
this.viewContainerRef, injector)).onDestroy(() => this.alarmReassigned.emit(true));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
@ -17,65 +17,80 @@
|
||||
-->
|
||||
|
||||
<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 }}
|
||||
<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="{{ 'alarm-comment.sort-direction' | translate }}"
|
||||
(click)="exportAlarmActivity()"
|
||||
matTooltip="{{ 'alarm-activity.export' | translate }}"
|
||||
matTooltipPosition="above">
|
||||
<mat-icon class="material-icons">{{ getSortDirectionIcon() }}</mat-icon>
|
||||
<mat-icon class="material-icons" svgIcon="mdi:file-export"></mat-icon>
|
||||
</button>
|
||||
<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-comment.refresh' | translate }}"
|
||||
matTooltip="{{ 'alarm-activity.refresh' | translate }}"
|
||||
matTooltipPosition="above">
|
||||
<mat-icon class="material-icons">refresh</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div fxLayout="column" fxLayoutGap="32px">
|
||||
<ng-container *ngIf="isDirectionDescending()">
|
||||
</div>
|
||||
<div fxLayout="column">
|
||||
<ng-container *ngIf="!isDirectionAscending()">
|
||||
<ng-container *ngTemplateOutlet="commentInput"></ng-container>
|
||||
</ng-container>
|
||||
<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 style="margin-left: 38px"
|
||||
*ngIf="displayDataElement.isSystemComment; else userComment">
|
||||
<span class="tb-alarm-comments-system-text"
|
||||
style="margin-right: 8px">
|
||||
<div class="system-comment" *ngIf="displayDataElement.isSystemComment; else userComment">
|
||||
<span class="system-text" style="margin-right: 8px">
|
||||
{{ displayDataElement.commentText }}
|
||||
</span>
|
||||
<span class="tb-alarm-comments-time">
|
||||
<span class="time" style="padding: 3px"
|
||||
[matTooltip]="displayDataElement.createdTime"
|
||||
matTooltipPosition="right">
|
||||
{{ displayDataElement.createdDateAgo }}
|
||||
</span>
|
||||
</div>
|
||||
<ng-template #userComment>
|
||||
<div fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="8px"
|
||||
<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="tb-alarm-comments-user-avatar"
|
||||
<div class="user-avatar" fxLayout="row" fxLayoutAlign="center center" fxFlexAlign="start" fxHide.xs
|
||||
[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">
|
||||
<span class="user-name">{{ displayDataElement.displayName }}</span>
|
||||
<span class="time"
|
||||
[matTooltip]="displayDataElement.createdTime"
|
||||
matTooltipPosition="right">
|
||||
{{ displayDataElement.createdDateAgo }}
|
||||
</span>
|
||||
<span class="time" *ngIf="displayDataElement.isEdited"
|
||||
matTooltip="{{ displayDataElement.editedDateAgo }} {{ displayDataElement.editedTime }}"
|
||||
matTooltipPosition="right">
|
||||
Edited
|
||||
</span>
|
||||
</div>
|
||||
<span class="tb-alarm-comments-text">{{ displayDataElement.commentText }}</span>
|
||||
<span class="text">{{ displayDataElement.commentText }}</span>
|
||||
</div>
|
||||
<div fxLayout="row" class="tb-alarm-comments-action-buttons"
|
||||
<div fxLayout="row" fxLayout.xs="column" class="action-buttons"
|
||||
[ngClass]="{ 'show-buttons': displayDataElement.showActions }">
|
||||
<button mat-icon-button
|
||||
type="button"
|
||||
@ -92,14 +107,14 @@
|
||||
<ng-template #commentEditing>
|
||||
<div fxLayoutAlign="row center" fxLayoutGap="8px">
|
||||
<div fxLayout="row" fxLayoutAlign="center center"
|
||||
class="tb-alarm-comments-user-avatar"
|
||||
class="user-avatar"
|
||||
[style.background-color]="displayDataElement.avatarBgColor">
|
||||
{{ getUserInitials(displayDataElement.displayName) }}
|
||||
</div>
|
||||
<mat-form-field fxFlex appearance="fill" class="mat-block">
|
||||
<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"
|
||||
@ -124,22 +139,25 @@
|
||||
</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 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="tb-alarm-comments-user-avatar"
|
||||
class="user-avatar"
|
||||
*ngIf="userDisplayName$ | async; let userDisplayName"
|
||||
[style.background-color]="getCurrentUserBgColor(userDisplayName)">
|
||||
{{ getUserInitials(userDisplayName) }}
|
||||
</div>
|
||||
<mat-form-field appearance="fill" fxFlex class="mat-block">
|
||||
<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"
|
||||
@ -158,5 +176,6 @@
|
||||
</button>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</div>
|
||||
</ng-template>
|
||||
</div>
|
||||
|
||||
@ -13,20 +13,95 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
@use '@angular/material' as mat;
|
||||
@import '../theme.scss';
|
||||
|
||||
$primary-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: $primary-color;
|
||||
max-width: 600px;
|
||||
width: 100%;
|
||||
|
||||
&-header {
|
||||
background-color: #fafafa;
|
||||
.comment-input {
|
||||
position: sticky;
|
||||
top: -25px;
|
||||
z-index: 1;
|
||||
margin-bottom: 10px;
|
||||
|
||||
&-title {
|
||||
.inner-wrap {
|
||||
border: $border;
|
||||
padding: 0 24px;
|
||||
background-color: $primary-color;
|
||||
}
|
||||
|
||||
&.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.header {
|
||||
z-index: 1;
|
||||
position: sticky;
|
||||
top: -24px;
|
||||
background-color: white;
|
||||
|
||||
&.activity-only {
|
||||
top: 0;
|
||||
}
|
||||
|
||||
&-container {
|
||||
padding: 0 24px;
|
||||
background-color: $primary-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;
|
||||
@ -36,46 +111,84 @@
|
||||
color: rgba(0, 0, 0, 0.38);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&-user-avatar {
|
||||
.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: $primary-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-comment {
|
||||
margin-left: 38px;
|
||||
@media #{$mat-xs} {
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.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);
|
||||
|
||||
@ -30,13 +30,17 @@ import { map } from 'rxjs/operators';
|
||||
import { AlarmComment, AlarmCommentInfo, AlarmCommentType } from '@shared/models/alarm.models';
|
||||
import { UtilsService } from '@core/services/utils.service';
|
||||
import { EntityType } from '@shared/models/entity-type.models';
|
||||
import { DatePipe } from '@angular/common';
|
||||
import { ImportExportService } from '@home/components/import-export/import-export.service';
|
||||
|
||||
interface AlarmCommentsDisplayData {
|
||||
commentId?: string,
|
||||
displayName?: string,
|
||||
createdTime: string,
|
||||
createdDateAgo?: string,
|
||||
edit?: boolean,
|
||||
isEdited?: boolean,
|
||||
editedTime?: string;
|
||||
editedDateAgo?: string,
|
||||
showActions?: boolean,
|
||||
commentText?: string,
|
||||
@ -54,7 +58,7 @@ export class AlarmCommentComponent implements OnInit {
|
||||
alarmId: string;
|
||||
|
||||
@Input()
|
||||
commentsHeaderEnabled: boolean = true;
|
||||
alarmActivityOnly: boolean = false;
|
||||
|
||||
authUser: AuthUser;
|
||||
|
||||
@ -85,7 +89,9 @@ export class AlarmCommentComponent implements OnInit {
|
||||
public fb: FormBuilder,
|
||||
private dialogService: DialogService,
|
||||
public dateAgoPipe: DateAgoPipe,
|
||||
private utilsService: UtilsService) {
|
||||
private utilsService: UtilsService,
|
||||
private datePipe: DatePipe,
|
||||
private importExportService: ImportExportService) {
|
||||
|
||||
this.authUser = getCurrentAuthUser(store);
|
||||
|
||||
@ -110,7 +116,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 = this.datePipe.transform(alarmComment.createdTime, 'yyyy-MM-dd HH:mm:ss');
|
||||
displayDataElement.createdDateAgo = this.dateAgoPipe.transform(alarmComment.createdTime);
|
||||
displayDataElement.commentText = alarmComment.comment.text;
|
||||
displayDataElement.isSystemComment = alarmComment.type === AlarmCommentType.SYSTEM;
|
||||
@ -119,7 +126,8 @@ 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 = this.datePipe.transform(alarmComment.comment.editedOn, 'yyyy-MM-dd HH:mm:ss');
|
||||
displayDataElement.editedDateAgo = this.dateAgoPipe.transform(alarmComment.comment.editedOn) + '\n';
|
||||
displayDataElement.showActions = false;
|
||||
displayDataElement.isSystemComment = false;
|
||||
displayDataElement.avatarBgColor = this.utilsService.stringToHslColor(displayDataElement.displayName,
|
||||
@ -137,6 +145,11 @@ export class AlarmCommentComponent implements OnInit {
|
||||
this.loadAlarmComments();
|
||||
}
|
||||
|
||||
exportAlarmActivity() {
|
||||
let fileName = this.translate.instant('alarm.alarm') + '_' + this.translate.instant('alarm-activity.activity');
|
||||
this.importExportService.exportCsv(this.getDataForExport(), fileName.toLowerCase());
|
||||
}
|
||||
|
||||
saveComment(): void {
|
||||
const commentInputValue: string = this.getAlarmCommentFormControl().value;
|
||||
if (commentInputValue) {
|
||||
@ -194,7 +207,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,17 +224,19 @@ 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'
|
||||
}
|
||||
|
||||
getSortDirectionTooltipText() {
|
||||
let text = this.alarmCommentSortOrder.direction === Direction.DESC ? 'alarm-activity.newest-first' :
|
||||
'alarm-activity.oldest-first';
|
||||
return this.translate.instant(text);
|
||||
}
|
||||
|
||||
isDirectionAscending() {
|
||||
return this.alarmCommentSortOrder.direction === Direction.ASC;
|
||||
}
|
||||
|
||||
isDirectionDescending() {
|
||||
return this.alarmCommentSortOrder.direction === Direction.DESC;
|
||||
}
|
||||
|
||||
onCommentMouseEnter(commentId: string, displayDataIndex: number): void {
|
||||
if (!this.editMode) {
|
||||
const alarmUserId = this.getAlarmCommentById(commentId).userId.id;
|
||||
@ -292,4 +307,19 @@ export class AlarmCommentComponent implements OnInit {
|
||||
return this.displayData.find(commentDisplayData => commentDisplayData.commentId === commentId);
|
||||
}
|
||||
|
||||
private getDataForExport() {
|
||||
let dataToExport = [];
|
||||
for (let row of this.displayData) {
|
||||
let exportRow = {
|
||||
[this.translate.instant('alarm-activity.author')]: row.isSystemComment ?
|
||||
this.translate.instant('alarm-activity.system') : row.displayName,
|
||||
[this.translate.instant('alarm-activity.created-date')]: row.createdTime,
|
||||
[this.translate.instant('alarm-activity.edited-date')]: row.editedTime,
|
||||
[this.translate.instant('alarm-activity.text')]: row.commentText
|
||||
}
|
||||
dataToExport.push(exportRow)
|
||||
}
|
||||
return dataToExport;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -28,23 +28,13 @@
|
||||
<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-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.severity</mat-label>
|
||||
<input matInput formControlName="alarmSeverity" readonly
|
||||
@ -52,66 +42,50 @@
|
||||
</mat-form-field>
|
||||
</div>
|
||||
<div fxLayout="row" fxLayoutGap="6px">
|
||||
<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
|
||||
</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 *ngIf="alarmFormGroup.get('duration').value" fxFlex class="mat-block">
|
||||
<mat-label translate>alarm.duration</mat-label>
|
||||
<input matInput formControlName="duration" 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>
|
||||
<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 *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 fxFlex class="mat-block">
|
||||
<mat-label translate>alarm.status</mat-label>
|
||||
<input matInput formControlName="alarmStatus" readonly>
|
||||
</mat-form-field>
|
||||
<span fxFlex *ngIf="!alarmFormGroup.get('ackTime').value || !alarmFormGroup.get('clearTime').value"></span>
|
||||
</div>
|
||||
<mat-expansion-panel class="tb-alarm-details">
|
||||
<mat-expansion-panel-header fxLayout="row wrap" style="margin-bottom: 8px">
|
||||
<mat-panel-title>
|
||||
</mat-panel-title>
|
||||
<mat-panel-description fxLayoutAlign="end center" translate>
|
||||
alarm.show-more
|
||||
</mat-panel-description>
|
||||
</mat-expansion-panel-header>
|
||||
<ng-template matExpansionPanelContent>
|
||||
<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 ||
|
||||
|
||||
@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
:host ::ng-deep {
|
||||
.assignee-field {
|
||||
.mat-form-field-label-wrapper {
|
||||
|
||||
@ -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: [''],
|
||||
startTime: [''],
|
||||
endTime: [''],
|
||||
ackTime: [''],
|
||||
clearTime: [''],
|
||||
type: [''],
|
||||
alarmSeverity: [''],
|
||||
startTime: [''],
|
||||
duration: [''],
|
||||
type: [''],
|
||||
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.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.ackTs) {
|
||||
this.alarmFormGroup.get('ackTime')
|
||||
.patchValue(this.datePipe.transform(alarm.ackTs, 'yyyy-MM-dd HH:mm:ss'));
|
||||
if (alarm.endTs && (alarm.status === AlarmStatus.CLEARED_ACK || alarm.status === AlarmStatus.CLEARED_UNACK)) {
|
||||
duration = this.millisecondsToTimeStringPipe.transform(alarm.endTs - alarm.startTs);
|
||||
}
|
||||
if (alarm.clearTs) {
|
||||
this.alarmFormGroup.get('clearTime')
|
||||
.patchValue(this.datePipe.transform(alarm.clearTs, 'yyyy-MM-dd HH:mm:ss'));
|
||||
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);
|
||||
|
||||
@ -53,7 +53,8 @@ import { Authority } from '@shared/models/authority.enum';
|
||||
import { ChangeDetectorRef, Injector, StaticProvider, ViewContainerRef } from '@angular/core';
|
||||
import { ConnectedPosition, Overlay, OverlayConfig, OverlayRef } from '@angular/cdk/overlay';
|
||||
import {
|
||||
ALARM_ASSIGNEE_PANEL_DATA, AlarmAssigneePanelComponent,
|
||||
ALARM_ASSIGNEE_PANEL_DATA,
|
||||
AlarmAssigneePanelComponent,
|
||||
AlarmAssigneePanelData
|
||||
} from '@home/components/alarm/alarm-assignee-panel.component';
|
||||
import { ComponentPortal } from '@angular/cdk/portal';
|
||||
@ -168,7 +169,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 +180,8 @@ export class AlarmTableConfig extends EntityTableConfig<AlarmInfo, TimePageLink>
|
||||
alarm: entity,
|
||||
allowAcknowledgment: isPermissionWrite,
|
||||
allowClear: isPermissionWrite,
|
||||
displayDetails: true
|
||||
displayDetails: true,
|
||||
allowAssign: true
|
||||
}
|
||||
}).afterClosed().subscribe(
|
||||
(res) => {
|
||||
@ -281,8 +283,13 @@ export class AlarmTableConfig extends EntityTableConfig<AlarmInfo, TimePageLink>
|
||||
}
|
||||
];
|
||||
const injector = Injector.create({parent: this.viewContainerRef.injector, providers});
|
||||
overlayRef.attach(new ComponentPortal(AlarmAssigneePanelComponent,
|
||||
this.viewContainerRef, injector)).onDestroy(() => this.updateData());
|
||||
const componentRef = overlayRef.attach(new ComponentPortal(AlarmAssigneePanelComponent,
|
||||
this.viewContainerRef, injector));
|
||||
componentRef.onDestroy(() => {
|
||||
if (componentRef.instance.reassigned) {
|
||||
this.updateData()
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -14,9 +14,12 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
:host ::ng-deep {
|
||||
tb-entities-table.tb-details-mode {
|
||||
tb-entities-table {
|
||||
&.tb-details-mode {
|
||||
.mat-drawer-container {
|
||||
background-color: white;
|
||||
}
|
||||
}
|
||||
.assignee-cell {
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
@ -52,4 +55,3 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -150,6 +150,11 @@ export const ZIP_TYPE: FileType = {
|
||||
extension: 'zip'
|
||||
};
|
||||
|
||||
export const CSV_TYPE: FileType = {
|
||||
mimeType: 'attachament/csv',
|
||||
extension: 'csv'
|
||||
};
|
||||
|
||||
export function convertCSVToJson(csvdata: string, config: CsvToJsonConfig,
|
||||
onError: (messageId: string, params?: any) => void): CsvToJsonResult | number {
|
||||
config = config || {};
|
||||
|
||||
@ -47,6 +47,7 @@ import { ItemBufferService, WidgetItem } from '@core/services/item-buffer.servic
|
||||
import {
|
||||
BulkImportRequest,
|
||||
BulkImportResult,
|
||||
CSV_TYPE,
|
||||
FileType,
|
||||
ImportWidgetResult,
|
||||
JSON_TYPE,
|
||||
@ -597,6 +598,35 @@ export class ImportExportService {
|
||||
);
|
||||
}
|
||||
|
||||
private processCSVCell(cellData: any): any {
|
||||
if (isString(cellData)) {
|
||||
let result = cellData.replace(/"/g, '""');
|
||||
if (result.search(/([",\n])/g) >= 0) {
|
||||
result = `"${result}"`;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
return cellData;
|
||||
}
|
||||
|
||||
public exportCsv(data: {[key: string]: any}[], filename: string) {
|
||||
let colsHead: string;
|
||||
let colsData: string;
|
||||
if (data && data.length) {
|
||||
colsHead = Object.keys(data[0]).map(key => [this.processCSVCell(key)]).join(';');
|
||||
colsData = data.map(obj => [
|
||||
Object.keys(obj).map(col => [
|
||||
this.processCSVCell(obj[col])
|
||||
]).join(';')
|
||||
]).join('\n');
|
||||
} else {
|
||||
colsHead = '';
|
||||
colsData = '';
|
||||
}
|
||||
const csvData = `${colsHead}\n${colsData}`;
|
||||
this.downloadFile(csvData, filename, CSV_TYPE);
|
||||
}
|
||||
|
||||
public exportText(data: string | Array<string>, filename: string) {
|
||||
let content = data;
|
||||
if (Array.isArray(data)) {
|
||||
|
||||
@ -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()
|
||||
}
|
||||
|
||||
@ -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 }}
|
||||
|
||||
@ -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, []],
|
||||
|
||||
@ -466,6 +466,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",
|
||||
@ -501,15 +502,24 @@
|
||||
"any-type": "Any type",
|
||||
"search-propagated-alarms": "Search propagated alarms",
|
||||
"comments": "Alarm comments",
|
||||
"advanced-info": "Advanced info"
|
||||
"show-more": "Show more",
|
||||
"additional-info": "Additional info"
|
||||
},
|
||||
"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",
|
||||
"activity": "Activity",
|
||||
"export": "Export to CSV",
|
||||
"author": "Author",
|
||||
"created-date": "Created date",
|
||||
"edited-date": "Edited date",
|
||||
"text": "Text",
|
||||
"system": "System"
|
||||
},
|
||||
"alias": {
|
||||
"add": "Add alias",
|
||||
@ -5042,7 +5052,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…
x
Reference in New Issue
Block a user