Merge branch 'alarmCommentsUI' of github.com:rusikv/thingsboard into alarmCommentsUI
This commit is contained in:
commit
d5d2b043a2
46
ui-ngx/src/app/core/http/alarm-comment.service.ts
Normal file
46
ui-ngx/src/app/core/http/alarm-comment.service.ts
Normal file
@ -0,0 +1,46 @@
|
||||
///
|
||||
/// Copyright © 2016-2023 The Thingsboard Authors
|
||||
///
|
||||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
/// you may not use this file except in compliance with the License.
|
||||
/// You may obtain a copy of the License at
|
||||
///
|
||||
/// http://www.apache.org/licenses/LICENSE-2.0
|
||||
///
|
||||
/// Unless required by applicable law or agreed to in writing, software
|
||||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
///
|
||||
|
||||
import { Injectable } from '@angular/core';
|
||||
import { defaultHttpOptionsFromConfig, RequestConfig } from './http-utils';
|
||||
import { Observable } from 'rxjs';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { PageLink } from '@shared/models/page/page-link';
|
||||
import { PageData } from '@shared/models/page/page-data';
|
||||
import { AlarmComment, AlarmCommentInfo } from '@shared/models/alarm.models';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class AlarmCommentService {
|
||||
|
||||
constructor(
|
||||
private http: HttpClient
|
||||
) { }
|
||||
|
||||
public saveAlarmComment(alarmId: string, alarmComment: AlarmComment, config?: RequestConfig): Observable<AlarmComment> {
|
||||
return this.http.post<AlarmComment>(`/api/alarm/${alarmId}/comment`, alarmComment, defaultHttpOptionsFromConfig(config));
|
||||
}
|
||||
|
||||
public getAlarmComments(alarmId: string, pageLink: PageLink, config?: RequestConfig): Observable<PageData<AlarmCommentInfo>> {
|
||||
return this.http.get<PageData<AlarmComment>>(`/api/alarm/${alarmId}/comment${pageLink.toQuery()}`, defaultHttpOptionsFromConfig(config));
|
||||
}
|
||||
|
||||
public deleteAlarmComments(alarmId: string, commentId: string, config?: RequestConfig): Observable<void> {
|
||||
return this.http.delete<void>(`/api/alarm/${alarmId}/comment/${commentId}`, defaultHttpOptionsFromConfig(config));
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,55 @@
|
||||
<!--
|
||||
|
||||
Copyright © 2016-2023 The Thingsboard Authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
-->
|
||||
<div style="min-width: 600px;">
|
||||
<mat-toolbar color="primary">
|
||||
<h2>{{ 'alarm.comments' | translate }}</h2>
|
||||
<span fxFlex></span>
|
||||
<button mat-icon-button
|
||||
(click)="close()"
|
||||
type="button">
|
||||
<mat-icon class="material-icons">close</mat-icon>
|
||||
</button>
|
||||
</mat-toolbar>
|
||||
<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: 0">
|
||||
<tb-alarm-comment #alarmCommentComponent [alarmId]="alarmId"
|
||||
[commentsHeaderEnabled]="commentsHeaderEnabled">
|
||||
</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>
|
||||
<button mat-raised-button color="primary"
|
||||
type="button"
|
||||
(click)="changeSortDirection()">
|
||||
{{ 'alarm-comment.sort-direction' | translate}}
|
||||
</button>
|
||||
<button mat-raised-button color="primary"
|
||||
type="button"
|
||||
(click)="refresh()">
|
||||
{{ 'alarm-comment.refresh' | translate}}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@ -0,0 +1,64 @@
|
||||
///
|
||||
/// Copyright © 2016-2023 The Thingsboard Authors
|
||||
///
|
||||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
/// you may not use this file except in compliance with the License.
|
||||
/// You may obtain a copy of the License at
|
||||
///
|
||||
/// http://www.apache.org/licenses/LICENSE-2.0
|
||||
///
|
||||
/// Unless required by applicable law or agreed to in writing, software
|
||||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
///
|
||||
|
||||
import { Component, Inject, ViewChild } from '@angular/core';
|
||||
import { DialogComponent } from '@shared/components/dialog.component';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { AppState } from '@core/core.state';
|
||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||
import { Router } from '@angular/router';
|
||||
import { AlarmInfo } from '@shared/models/alarm.models';
|
||||
import { AlarmCommentComponent } from '@home/components/alarm/alarm-comment.component';
|
||||
|
||||
export interface AlarmCommentDialogData {
|
||||
alarmId?: string;
|
||||
alarm?: AlarmInfo;
|
||||
commentsHeaderEnabled: boolean;
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'tb-alarm-comment-dialog',
|
||||
templateUrl: './alarm-comment-dialog.component.html',
|
||||
styleUrls: []
|
||||
})
|
||||
export class AlarmCommentDialogComponent extends DialogComponent<AlarmCommentDialogComponent, void> {
|
||||
|
||||
alarmId: string;
|
||||
commentsHeaderEnabled: boolean = false;
|
||||
|
||||
@ViewChild('alarmCommentComponent', { static: true }) alarmCommentComponent: AlarmCommentComponent;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
changeSortDirection() {
|
||||
this.alarmCommentComponent.changeSortDirection();
|
||||
}
|
||||
|
||||
refresh() {
|
||||
this.alarmCommentComponent.loadAlarmComments();
|
||||
}
|
||||
|
||||
close(): void {
|
||||
this.dialogRef.close();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,161 @@
|
||||
<!--
|
||||
|
||||
Copyright © 2016-2023 The Thingsboard Authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
-->
|
||||
|
||||
<div fxFlexFill [formGroup]="alarmCommentFormGroup" class="tb-alarm-comments" fxLayout="column">
|
||||
<div *ngIf="commentsHeaderEnabled" class="tb-alarm-comments-header" fxLayout="row" fxLayoutAlign="space-between baseline">
|
||||
<span class="tb-alarm-comments-header-title">{{ 'alarm-comment.comments' | translate }}</span>
|
||||
<div>
|
||||
<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>
|
||||
</div>
|
||||
<div fxLayout="column" fxLayoutGap="32px">
|
||||
<ng-container *ngIf="isDirectionDescending()">
|
||||
<ng-container *ngTemplateOutlet="commentInput"></ng-container>
|
||||
</ng-container>
|
||||
<div fxFlex *ngFor="let displayDataElement of displayData; let i = index">
|
||||
<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-button mat-icon-button
|
||||
type="button"
|
||||
(click)="editComment(displayDataElement.commentId)">
|
||||
<mat-icon class="material-icons">edit</mat-icon>
|
||||
</button>
|
||||
<button mat-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="tb-alarm-comments-user-avatar"
|
||||
[style.background-color]="displayDataElement.avatarBgColor">
|
||||
{{ getUserInitials(displayDataElement.displayName) }}
|
||||
</div>
|
||||
<mat-form-field fxFlex appearance="standard" 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>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</ng-template>
|
||||
</ng-template>
|
||||
</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="standard" fxFlex class="mat-block">
|
||||
<textarea matInput
|
||||
type="text"
|
||||
placeholder="{{ 'alarm-comment.add' | translate }}"
|
||||
cdkTextareaAutosize
|
||||
cdkAutosizeMinRows="1"
|
||||
cols="1"
|
||||
formControlName="alarmComment"
|
||||
(keyup.enter)="saveComment()"
|
||||
(keydown.enter)="$event.preventDefault()">
|
||||
</textarea>
|
||||
<button mat-button
|
||||
mat-icon-button
|
||||
type="button"
|
||||
matSuffix
|
||||
*ngIf="getAlarmCommentFormControl().value"
|
||||
(click)="saveComment()">
|
||||
<mat-icon color="primary">
|
||||
send
|
||||
</mat-icon>
|
||||
</button>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</ng-template>
|
||||
</div>
|
||||
@ -0,0 +1,107 @@
|
||||
/**
|
||||
* Copyright © 2016-2023 The Thingsboard Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
:host {
|
||||
.tb-alarm-comments {
|
||||
padding: 16px 24px 24px 24px;
|
||||
background-color: #fafafa;
|
||||
max-width: 600px;
|
||||
|
||||
&-header {
|
||||
background-color: #fafafa;
|
||||
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;
|
||||
}
|
||||
|
||||
.mat-icon {
|
||||
color: rgba(0, 0, 0, 0.38);
|
||||
}
|
||||
}
|
||||
|
||||
&-user-avatar {
|
||||
width: 28px;
|
||||
min-width: 28px;
|
||||
height: 28px;
|
||||
min-height: 28px;
|
||||
border-radius: 50%;
|
||||
font-weight: 700;
|
||||
color: #FFFFFF;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
&-user-name {
|
||||
font-size: 16px;
|
||||
color: rgba(0, 0, 0, 0.76);
|
||||
font-weight: 500;
|
||||
letter-spacing: 0.25px;
|
||||
}
|
||||
|
||||
&-time {
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
color: rgba(0, 0, 0, 0.38);
|
||||
letter-spacing: 0.2px
|
||||
}
|
||||
|
||||
&-system-text {
|
||||
color: rgba(0, 0, 0, 0.38);
|
||||
font-weight: 500;
|
||||
letter-spacing: 0.25px;
|
||||
}
|
||||
|
||||
&-text {
|
||||
white-space: pre-line;
|
||||
word-break: break-word;
|
||||
color: rgba(0, 0, 0, 0.54);
|
||||
letter-spacing: 0.15px;
|
||||
}
|
||||
|
||||
&-action-buttons {
|
||||
visibility: hidden;
|
||||
.mat-icon {
|
||||
color: rgba(0, 0, 0, 0.38);
|
||||
}
|
||||
}
|
||||
|
||||
.show-buttons {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.green-button {
|
||||
color: #00695C;
|
||||
}
|
||||
|
||||
.red-button {
|
||||
color: #D12730;
|
||||
}
|
||||
|
||||
.mat-form-field {
|
||||
font-size: 16px;
|
||||
letter-spacing: 0.15px;
|
||||
color: rgba(0, 0, 0, 0.76);
|
||||
}
|
||||
|
||||
textarea {
|
||||
letter-spacing: 0.15px;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,295 @@
|
||||
///
|
||||
/// Copyright © 2016-2023 The Thingsboard Authors
|
||||
///
|
||||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
/// you may not use this file except in compliance with the License.
|
||||
/// You may obtain a copy of the License at
|
||||
///
|
||||
/// http://www.apache.org/licenses/LICENSE-2.0
|
||||
///
|
||||
/// Unless required by applicable law or agreed to in writing, software
|
||||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
///
|
||||
|
||||
import { Component, Input, OnInit } from '@angular/core';
|
||||
import { select, Store } from '@ngrx/store';
|
||||
import { AppState } from '@core/core.state';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { AlarmCommentService } from '@core/http/alarm-comment.service';
|
||||
import { AbstractControl, FormBuilder, FormGroup } from '@angular/forms';
|
||||
import { DialogService } from '@core/services/dialog.service';
|
||||
import { AuthUser, User } from '@shared/models/user.model';
|
||||
import { getCurrentAuthUser, selectUserDetails } from '@core/auth/auth.selectors';
|
||||
import { Direction, SortOrder } from '@shared/models/page/sort-order';
|
||||
import { MAX_SAFE_PAGE_SIZE, PageLink } from '@shared/models/page/page-link';
|
||||
import { DateAgoPipe } from '@shared/pipe/date-ago.pipe';
|
||||
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';
|
||||
|
||||
interface AlarmCommentsDisplayData {
|
||||
commentId?: string,
|
||||
displayName?: string,
|
||||
createdDateAgo?: string,
|
||||
edit?: boolean,
|
||||
isEdited?: boolean,
|
||||
editedDateAgo?: string,
|
||||
showActions?: boolean,
|
||||
commentText?: string,
|
||||
isSystemComment?: boolean,
|
||||
avatarBgColor?: string
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'tb-alarm-comment',
|
||||
templateUrl: './alarm-comment.component.html',
|
||||
styleUrls: ['./alarm-comment.component.scss']
|
||||
})
|
||||
export class AlarmCommentComponent implements OnInit {
|
||||
@Input()
|
||||
alarmId: string;
|
||||
|
||||
@Input()
|
||||
commentsHeaderEnabled: boolean = true;
|
||||
|
||||
authUser: AuthUser;
|
||||
|
||||
alarmCommentFormGroup: FormGroup;
|
||||
|
||||
alarmComments: Array<AlarmComment>;
|
||||
|
||||
displayData: Array<AlarmCommentsDisplayData> = new Array<AlarmCommentsDisplayData>();
|
||||
|
||||
alarmCommentSortOrder: SortOrder = {
|
||||
property: 'createdTime',
|
||||
direction: Direction.DESC
|
||||
};
|
||||
|
||||
editMode: boolean = false;
|
||||
|
||||
userDisplayName$ = this.store.pipe(
|
||||
select(selectUserDetails),
|
||||
map((user) => this.getUserDisplayName(user))
|
||||
);
|
||||
|
||||
currentUserDisplayName: string;
|
||||
currentUserAvatarColor: string;
|
||||
|
||||
constructor(protected store: Store<AppState>,
|
||||
private translate: TranslateService,
|
||||
private alarmCommentService: AlarmCommentService,
|
||||
public fb: FormBuilder,
|
||||
private dialogService: DialogService,
|
||||
public dateAgoPipe: DateAgoPipe,
|
||||
private utilsService: UtilsService) {
|
||||
|
||||
this.authUser = getCurrentAuthUser(store);
|
||||
|
||||
this.alarmCommentFormGroup = this.fb.group(
|
||||
{
|
||||
alarmCommentEdit: [''],
|
||||
alarmComment: ['']
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.loadAlarmComments();
|
||||
this.currentUserAvatarColor = this.utilsService.stringToHslColor(this.currentUserDisplayName,
|
||||
60, 40);
|
||||
}
|
||||
|
||||
loadAlarmComments(): void {
|
||||
this.alarmCommentService.getAlarmComments(this.alarmId, new PageLink(MAX_SAFE_PAGE_SIZE, 0, null,
|
||||
this.alarmCommentSortOrder)).subscribe(
|
||||
(pagedData) => {
|
||||
this.alarmComments = pagedData.data;
|
||||
this.displayData.length = 0;
|
||||
for (let alarmComment of pagedData.data) {
|
||||
let displayDataElement: AlarmCommentsDisplayData = {};
|
||||
displayDataElement.createdDateAgo = this.dateAgoPipe.transform(alarmComment.createdTime);
|
||||
displayDataElement.commentText = alarmComment.comment.text;
|
||||
displayDataElement.isSystemComment = alarmComment.type === AlarmCommentType.SYSTEM;
|
||||
if (alarmComment.type === AlarmCommentType.OTHER) {
|
||||
displayDataElement.commentId = alarmComment.id.id;
|
||||
displayDataElement.displayName = this.getUserDisplayName(alarmComment);
|
||||
displayDataElement.edit = false;
|
||||
displayDataElement.isEdited = alarmComment.comment.edited;
|
||||
displayDataElement.editedDateAgo = this.dateAgoPipe.transform(alarmComment.comment.editedOn).toLowerCase();
|
||||
displayDataElement.showActions = false;
|
||||
displayDataElement.isSystemComment = false;
|
||||
displayDataElement.avatarBgColor = this.utilsService.stringToHslColor(displayDataElement.displayName,
|
||||
40, 60);
|
||||
}
|
||||
this.displayData.push(displayDataElement);
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
changeSortDirection() {
|
||||
let currentDirection = this.alarmCommentSortOrder.direction;
|
||||
this.alarmCommentSortOrder.direction = currentDirection === Direction.DESC ? Direction.ASC : Direction.DESC;
|
||||
this.loadAlarmComments();
|
||||
}
|
||||
|
||||
saveComment(): void {
|
||||
const commentInputValue: string = this.getAlarmCommentFormControl().value;
|
||||
if (commentInputValue) {
|
||||
const comment: AlarmComment = {
|
||||
alarmId: {
|
||||
id: this.alarmId,
|
||||
entityType: EntityType.ALARM
|
||||
},
|
||||
type: AlarmCommentType.OTHER,
|
||||
comment: {
|
||||
text: commentInputValue
|
||||
}
|
||||
}
|
||||
this.doSave(comment);
|
||||
this.clearCommentInput();
|
||||
}
|
||||
}
|
||||
|
||||
saveEditedComment(commentId: string): void {
|
||||
const commentEditInputValue: string = this.getAlarmCommentEditFormControl().value;
|
||||
if (commentEditInputValue) {
|
||||
const editedComment: AlarmComment = this.getAlarmCommentById(commentId);
|
||||
editedComment.comment.text = commentEditInputValue;
|
||||
this.doSave(editedComment);
|
||||
this.clearCommentEditInput();
|
||||
this.editMode = false;
|
||||
this.getAlarmCommentFormControl().enable({emitEvent: false});
|
||||
}
|
||||
}
|
||||
|
||||
private doSave(comment: AlarmComment): void {
|
||||
this.alarmCommentService.saveAlarmComment(this.alarmId, comment).subscribe(
|
||||
() => {
|
||||
this.loadAlarmComments();
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
editComment(commentId: string): void {
|
||||
const commentDisplayData = this.getDataElementByCommentId(commentId);
|
||||
commentDisplayData.edit = true;
|
||||
this.editMode = true;
|
||||
this.getAlarmCommentEditFormControl().patchValue(commentDisplayData.commentText);
|
||||
this.getAlarmCommentFormControl().disable({emitEvent: false});
|
||||
}
|
||||
|
||||
cancelEdit(commentId: string): void {
|
||||
const commentDisplayData = this.getDataElementByCommentId(commentId);
|
||||
commentDisplayData.edit = false;
|
||||
this.editMode = false;
|
||||
this.getAlarmCommentFormControl().enable({emitEvent: false});
|
||||
}
|
||||
|
||||
deleteComment(commentId: string): void {
|
||||
const alarmCommentInfo: AlarmComment = this.getAlarmCommentById(commentId);
|
||||
const commentText: string = alarmCommentInfo.comment.text;
|
||||
this.dialogService.confirm(
|
||||
this.translate.instant('alarm-comment.delete-alarm-comment'),
|
||||
commentText,
|
||||
this.translate.instant('action.cancel'),
|
||||
this.translate.instant('action.delete')).subscribe(
|
||||
(result) => {
|
||||
if (result) {
|
||||
this.alarmCommentService.deleteAlarmComments(this.alarmId, commentId).subscribe(
|
||||
() => {
|
||||
this.loadAlarmComments();
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
getSortDirectionIcon() {
|
||||
return this.alarmCommentSortOrder.direction === Direction.DESC ? 'arrow_downward' : 'arrow_upward'
|
||||
}
|
||||
|
||||
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;
|
||||
if (this.authUser.userId === alarmUserId) {
|
||||
this.displayData[displayDataIndex].showActions = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onCommentMouseLeave(displayDataIndex: number): void {
|
||||
this.displayData[displayDataIndex].showActions = false;
|
||||
}
|
||||
|
||||
getUserInitials(userName: string): string {
|
||||
let initials = '';
|
||||
const userNameSplit = userName.split(' ');
|
||||
initials += userNameSplit[0].charAt(0).toUpperCase();
|
||||
if (userNameSplit.length > 1) {
|
||||
initials += userNameSplit[userNameSplit.length - 1].charAt(0).toUpperCase();
|
||||
}
|
||||
return initials;
|
||||
}
|
||||
|
||||
getCurrentUserBgColor(userDisplayName: string) {
|
||||
return this.utilsService.stringToHslColor(userDisplayName, 40, 60);
|
||||
}
|
||||
|
||||
private getUserDisplayName(alarmCommentInfo: AlarmCommentInfo | User): string {
|
||||
let name = '';
|
||||
if ((alarmCommentInfo.firstName && alarmCommentInfo.firstName.length > 0) ||
|
||||
(alarmCommentInfo.lastName && alarmCommentInfo.lastName.length > 0)) {
|
||||
if (alarmCommentInfo.firstName) {
|
||||
name += alarmCommentInfo.firstName;
|
||||
}
|
||||
if (alarmCommentInfo.lastName) {
|
||||
if (name.length > 0) {
|
||||
name += ' ';
|
||||
}
|
||||
name += alarmCommentInfo.lastName;
|
||||
}
|
||||
} else {
|
||||
name = alarmCommentInfo.email;
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
getAlarmCommentFormControl(): AbstractControl {
|
||||
return this.alarmCommentFormGroup.get('alarmComment');
|
||||
}
|
||||
|
||||
getAlarmCommentEditFormControl(): AbstractControl {
|
||||
return this.alarmCommentFormGroup.get('alarmCommentEdit');
|
||||
}
|
||||
|
||||
private clearCommentInput(): void {
|
||||
this.getAlarmCommentFormControl().patchValue('');
|
||||
}
|
||||
|
||||
private clearCommentEditInput(): void {
|
||||
this.getAlarmCommentEditFormControl().patchValue('');
|
||||
}
|
||||
|
||||
private getAlarmCommentById(id: string): AlarmComment {
|
||||
return this.alarmComments.find(comment => comment.id.id === id);
|
||||
}
|
||||
|
||||
private getDataElementByCommentId(commentId: string): AlarmCommentsDisplayData {
|
||||
return this.displayData.find(commentDisplayData => commentDisplayData.commentId === commentId);
|
||||
}
|
||||
|
||||
}
|
||||
@ -107,6 +107,7 @@
|
||||
</ng-template>
|
||||
</mat-expansion-panel>
|
||||
</fieldset>
|
||||
<tb-alarm-comment #alarmCommentComponent [alarmId]="alarmId"></tb-alarm-comment>
|
||||
</div>
|
||||
<div mat-dialog-actions fxLayout="row">
|
||||
<button mat-button color="primary"
|
||||
|
||||
@ -14,7 +14,7 @@
|
||||
/// limitations under the License.
|
||||
///
|
||||
|
||||
import { Component, Inject, OnInit } from '@angular/core';
|
||||
import { Component, Inject, OnInit, ViewChild } from '@angular/core';
|
||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { AppState } from '@core/core.state';
|
||||
@ -33,6 +33,7 @@ import { AlarmService } from '@core/http/alarm.service';
|
||||
import { tap } from 'rxjs/operators';
|
||||
import { DatePipe } from '@angular/common';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { AlarmCommentComponent } from '@home/components/alarm/alarm-comment.component';
|
||||
import { UtilsService } from '@core/services/utils.service';
|
||||
|
||||
export interface AlarmDetailsDialogData {
|
||||
@ -67,6 +68,8 @@ export class AlarmDetailsDialogComponent extends DialogComponent<AlarmDetailsDia
|
||||
|
||||
alarmUpdated = false;
|
||||
|
||||
@ViewChild('alarmCommentComponent', { static: true }) alarmCommentComponent: AlarmCommentComponent;
|
||||
|
||||
assigneeInitials = '';
|
||||
|
||||
constructor(protected store: Store<AppState>,
|
||||
@ -172,6 +175,7 @@ export class AlarmDetailsDialogComponent extends DialogComponent<AlarmDetailsDia
|
||||
() => {
|
||||
this.alarmUpdated = true;
|
||||
this.loadAlarm();
|
||||
this.alarmCommentComponent.loadAlarmComments();
|
||||
}
|
||||
);
|
||||
}
|
||||
@ -183,6 +187,7 @@ export class AlarmDetailsDialogComponent extends DialogComponent<AlarmDetailsDia
|
||||
() => {
|
||||
this.alarmUpdated = true;
|
||||
this.loadAlarm();
|
||||
this.alarmCommentComponent.loadAlarmComments();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@ -19,6 +19,8 @@ import { CommonModule } from '@angular/common';
|
||||
import { SharedModule } from '@app/shared/shared.module';
|
||||
import { AlarmDetailsDialogComponent } from '@home/components/alarm/alarm-details-dialog.component';
|
||||
import { SHARED_HOME_COMPONENTS_MODULE_TOKEN } from '@home/components/tokens';
|
||||
import { AlarmCommentComponent } from '@home/components/alarm/alarm-comment.component';
|
||||
import { AlarmCommentDialogComponent } from '@home/components/alarm/alarm-comment-dialog.component';
|
||||
|
||||
@NgModule({
|
||||
providers: [
|
||||
@ -26,14 +28,18 @@ import { SHARED_HOME_COMPONENTS_MODULE_TOKEN } from '@home/components/tokens';
|
||||
],
|
||||
declarations:
|
||||
[
|
||||
AlarmDetailsDialogComponent
|
||||
AlarmDetailsDialogComponent,
|
||||
AlarmCommentComponent,
|
||||
AlarmCommentDialogComponent
|
||||
],
|
||||
imports: [
|
||||
CommonModule,
|
||||
SharedModule
|
||||
],
|
||||
exports: [
|
||||
AlarmDetailsDialogComponent
|
||||
AlarmDetailsDialogComponent,
|
||||
AlarmCommentComponent,
|
||||
AlarmCommentDialogComponent
|
||||
]
|
||||
})
|
||||
export class SharedHomeComponentsModule { }
|
||||
|
||||
@ -131,6 +131,10 @@ import {
|
||||
ALARM_ASSIGNEE_PANEL_DATA, AlarmAssigneePanelComponent,
|
||||
AlarmAssigneePanelData
|
||||
} from '@home/components/alarm/alarm-assignee-panel.component';
|
||||
import {
|
||||
AlarmCommentDialogComponent,
|
||||
AlarmCommentDialogData
|
||||
} from '@home/components/alarm/alarm-comment-dialog.component';
|
||||
|
||||
interface AlarmsTableWidgetSettings extends TableWidgetSettings {
|
||||
alarmsTitle: string;
|
||||
@ -143,12 +147,14 @@ interface AlarmsTableWidgetSettings extends TableWidgetSettings {
|
||||
allowAcknowledgment: boolean;
|
||||
allowClear: boolean;
|
||||
allowAssign: boolean;
|
||||
displayComments: boolean;
|
||||
}
|
||||
|
||||
interface AlarmWidgetActionDescriptor extends TableCellButtonActionDescriptor {
|
||||
details?: boolean;
|
||||
acknowledge?: boolean;
|
||||
clear?: boolean;
|
||||
comments?: boolean;
|
||||
}
|
||||
|
||||
@Component({
|
||||
@ -195,6 +201,7 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit,
|
||||
public allowAcknowledgment = true;
|
||||
private allowClear = true;
|
||||
public allowAssign = true;
|
||||
private displayComments = false;
|
||||
|
||||
private defaultPageSize = 10;
|
||||
private defaultSortOrder = '-' + alarmFields.createdTime.value;
|
||||
@ -331,6 +338,7 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit,
|
||||
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;
|
||||
this.displayComments = isDefined(this.settings.displayComments) ? this.settings.displayComments : false;
|
||||
this.allowAssign = isDefined(this.settings.allowAssign) ? this.settings.allowAssign : true;
|
||||
|
||||
if (this.settings.alarmsTitle && this.settings.alarmsTitle.length) {
|
||||
@ -486,6 +494,16 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit,
|
||||
);
|
||||
}
|
||||
|
||||
if (this.displayComments) {
|
||||
actionCellDescriptors.push(
|
||||
{
|
||||
displayName: this.translate.instant('alarm-comment.comments'),
|
||||
icon: 'comment',
|
||||
comments: true
|
||||
} as AlarmWidgetActionDescriptor
|
||||
);
|
||||
}
|
||||
|
||||
this.setCellButtonAction = !!(actionCellDescriptors.length + this.ctx.actionsApi.getActionDescriptors('actionCellButton').length);
|
||||
|
||||
if (this.setCellButtonAction) {
|
||||
@ -801,6 +819,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 ($event) {
|
||||
$event.stopPropagation();
|
||||
@ -964,6 +984,24 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit,
|
||||
}
|
||||
}
|
||||
|
||||
private openAlarmComments($event: Event, alarm: AlarmDataInfo) {
|
||||
if ($event) {
|
||||
$event.stopPropagation();
|
||||
}
|
||||
if (alarm && alarm.id && alarm.id.id !== NULL_UUID) {
|
||||
this.dialog.open<AlarmCommentDialogComponent, AlarmCommentDialogData, void>
|
||||
(AlarmCommentDialogComponent,
|
||||
{
|
||||
disableClose: true,
|
||||
panelClass: ['tb-dialog', 'tb-fullscreen-dialog'],
|
||||
data: {
|
||||
alarmId: alarm.id.id,
|
||||
commentsHeaderEnabled: false
|
||||
}
|
||||
}).afterClosed()
|
||||
}
|
||||
}
|
||||
|
||||
private defaultContent(key: EntityColumn, contentInfo: CellContentInfo, value: any): any {
|
||||
if (isDefined(value)) {
|
||||
const alarmField = alarmFields[key.name];
|
||||
|
||||
@ -70,6 +70,9 @@
|
||||
<mat-slide-toggle formControlName="allowAssign">
|
||||
{{ 'widgets.table.allow-alarms-assign' | translate }}
|
||||
</mat-slide-toggle>
|
||||
<mat-slide-toggle formControlName="displayComments">
|
||||
{{ 'widgets.table.display-alarm-comments' | translate }}
|
||||
</mat-slide-toggle>
|
||||
<section fxLayout="column" fxLayout.gt-xs="row" fxLayoutGap="8px" fxLayoutAlign.gt-xs="start center">
|
||||
<mat-slide-toggle fxFlex formControlName="displayPagination">
|
||||
{{ 'widgets.table.display-pagination' | translate }}
|
||||
|
||||
@ -74,6 +74,7 @@ export class AlarmsTableWidgetSettingsComponent extends WidgetSettingsComponent
|
||||
allowAcknowledgment: [settings.allowAcknowledgment, []],
|
||||
allowClear: [settings.allowClear, []],
|
||||
allowAssign: [settings.allowAssign, []],
|
||||
displayComments: [settings.displayComments, []],
|
||||
displayPagination: [settings.displayPagination, []],
|
||||
defaultPageSize: [settings.defaultPageSize, [Validators.min(1)]],
|
||||
defaultSortOrder: [settings.defaultSortOrder, []],
|
||||
|
||||
@ -23,7 +23,8 @@ import { NULL_UUID } from '@shared/models/id/has-uuid';
|
||||
import { EntityType } from '@shared/models/entity-type.models';
|
||||
import { CustomerId } from '@shared/models/id/customer-id';
|
||||
import { TableCellButtonActionDescriptor } from '@home/components/widget/lib/table-widget.models';
|
||||
import { UserId } from "@shared/models/id/user-id";
|
||||
import { AlarmCommentId } from '@shared/models/id/alarm-comment-id';
|
||||
import { UserId } from '@shared/models/id/user-id';
|
||||
|
||||
export enum AlarmSeverity {
|
||||
CRITICAL = 'CRITICAL',
|
||||
@ -104,6 +105,28 @@ export interface Alarm extends BaseData<AlarmId> {
|
||||
details?: any;
|
||||
}
|
||||
|
||||
export enum AlarmCommentType {
|
||||
SYSTEM = 'SYSTEM',
|
||||
OTHER = 'OTHER'
|
||||
}
|
||||
|
||||
export interface AlarmComment extends BaseData<AlarmCommentId> {
|
||||
alarmId: AlarmId;
|
||||
userId?: UserId;
|
||||
type: AlarmCommentType;
|
||||
comment: {
|
||||
text: string;
|
||||
edited?: boolean;
|
||||
editedOn?: number;
|
||||
}
|
||||
}
|
||||
|
||||
export interface AlarmCommentInfo extends AlarmComment {
|
||||
firstName?: string;
|
||||
lastName?: string;
|
||||
email?: string;
|
||||
}
|
||||
|
||||
export interface AlarmInfo extends Alarm {
|
||||
originatorName: string;
|
||||
originatorLabel: string;
|
||||
|
||||
24
ui-ngx/src/app/shared/models/id/alarm-comment-id.ts
Normal file
24
ui-ngx/src/app/shared/models/id/alarm-comment-id.ts
Normal file
@ -0,0 +1,24 @@
|
||||
///
|
||||
/// Copyright © 2016-2023 The Thingsboard Authors
|
||||
///
|
||||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
/// you may not use this file except in compliance with the License.
|
||||
/// You may obtain a copy of the License at
|
||||
///
|
||||
/// http://www.apache.org/licenses/LICENSE-2.0
|
||||
///
|
||||
/// Unless required by applicable law or agreed to in writing, software
|
||||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
///
|
||||
|
||||
import { HasUUID } from '@shared/models/id/has-uuid';
|
||||
|
||||
export class AlarmCommentId implements HasUUID {
|
||||
id: string;
|
||||
constructor(id: string) {
|
||||
this.id = id;
|
||||
}
|
||||
}
|
||||
58
ui-ngx/src/app/shared/pipe/date-ago.pipe.ts
Normal file
58
ui-ngx/src/app/shared/pipe/date-ago.pipe.ts
Normal file
@ -0,0 +1,58 @@
|
||||
///
|
||||
/// Copyright © 2016-2023 The Thingsboard Authors
|
||||
///
|
||||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
/// you may not use this file except in compliance with the License.
|
||||
/// You may obtain a copy of the License at
|
||||
///
|
||||
/// http://www.apache.org/licenses/LICENSE-2.0
|
||||
///
|
||||
/// Unless required by applicable law or agreed to in writing, software
|
||||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
///
|
||||
|
||||
import { Inject, Pipe, PipeTransform } from '@angular/core';
|
||||
import { DAY, HOUR, MINUTE, SECOND, WEEK, YEAR } from '@shared/models/time/time.models';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
|
||||
const intervals = {
|
||||
years: YEAR,
|
||||
months: DAY * 30,
|
||||
weeks: WEEK,
|
||||
days: DAY,
|
||||
hr: HOUR,
|
||||
min: MINUTE,
|
||||
sec: SECOND
|
||||
};
|
||||
|
||||
@Pipe({
|
||||
name: 'dateAgo'
|
||||
})
|
||||
export class DateAgoPipe implements PipeTransform {
|
||||
|
||||
constructor(@Inject(TranslateService) private translate: TranslateService) {
|
||||
|
||||
}
|
||||
|
||||
transform(value: number): string {
|
||||
if (value) {
|
||||
const ms = Math.floor((+new Date() - +new Date(value)));
|
||||
if (ms < 29 * SECOND) { // less than 30 seconds ago will show as 'Just now'
|
||||
return this.translate.instant('timewindow.just-now');
|
||||
}
|
||||
let counter;
|
||||
// tslint:disable-next-line:forin
|
||||
for (const i in intervals) {
|
||||
counter = Math.floor(ms / intervals[i]);
|
||||
if (counter > 0) {
|
||||
return this.translate.instant(`timewindow.${i}`, {[i]: counter});
|
||||
}
|
||||
}
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
}
|
||||
@ -169,6 +169,7 @@ import { PhoneInputComponent } from '@shared/components/phone-input.component';
|
||||
import { CustomDateAdapter } from '@shared/adapter/custom-datatime-adapter';
|
||||
import { CustomPaginatorIntl } from '@shared/services/custom-paginator-intl';
|
||||
import { TbScriptLangComponent } from '@shared/components/script-lang.component';
|
||||
import { DateAgoPipe } from '@shared/pipe/date-ago.pipe';
|
||||
|
||||
export function MarkedOptionsFactory(markedOptionsService: MarkedOptionsService) {
|
||||
return markedOptionsService;
|
||||
@ -184,6 +185,7 @@ export function MarkedOptionsFactory(markedOptionsService: MarkedOptionsService)
|
||||
TbJsonPipe,
|
||||
FileSizePipe,
|
||||
SafePipe,
|
||||
DateAgoPipe,
|
||||
{
|
||||
provide: FlowInjectionToken,
|
||||
useValue: Flow
|
||||
@ -295,7 +297,8 @@ export function MarkedOptionsFactory(markedOptionsService: MarkedOptionsService)
|
||||
ProtobufContentComponent,
|
||||
BranchAutocompleteComponent,
|
||||
PhoneInputComponent,
|
||||
TbScriptLangComponent
|
||||
TbScriptLangComponent,
|
||||
DateAgoPipe
|
||||
],
|
||||
imports: [
|
||||
CommonModule,
|
||||
@ -500,7 +503,8 @@ export function MarkedOptionsFactory(markedOptionsService: MarkedOptionsService)
|
||||
ProtobufContentComponent,
|
||||
BranchAutocompleteComponent,
|
||||
PhoneInputComponent,
|
||||
TbScriptLangComponent
|
||||
TbScriptLangComponent,
|
||||
DateAgoPipe
|
||||
]
|
||||
})
|
||||
export class SharedModule { }
|
||||
|
||||
@ -451,7 +451,6 @@
|
||||
"end-time": "End time",
|
||||
"ack-time": "Acknowledged time",
|
||||
"clear-time": "Cleared time",
|
||||
"assign-time": "Assign time",
|
||||
"alarm-severity-list": "Alarm severity list",
|
||||
"any-severity": "Any severity",
|
||||
"severity-critical": "Critical",
|
||||
@ -486,8 +485,17 @@
|
||||
"alarm-type-list": "Alarm type list",
|
||||
"any-type": "Any type",
|
||||
"search-propagated-alarms": "Search propagated alarms",
|
||||
"comments": "Alarm comments",
|
||||
"advanced-info": "Advanced info"
|
||||
},
|
||||
"alarm-comment": {
|
||||
"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"
|
||||
},
|
||||
"alias": {
|
||||
"add": "Add alias",
|
||||
"edit": "Edit alias",
|
||||
@ -3397,16 +3405,16 @@
|
||||
"days": "Days"
|
||||
},
|
||||
"timewindow": {
|
||||
"years": "{ years, plural, 1 { year } other {# years } }",
|
||||
"months": "{ months, plural, 1 { month } other {# months } }",
|
||||
"weeks": "{ weeks, plural, 1 { week } other {# weeks } }",
|
||||
"days": "{ days, plural, 1 { day } other {# days } }",
|
||||
"hours": "{ hours, plural, 0 { hour } 1 {1 hour } other {# hours } }",
|
||||
"hr": "{{ hr }} hr",
|
||||
"minutes": "{ minutes, plural, 0 { minute } 1 {1 minute } other {# minutes } }",
|
||||
"min": "{{ min }} min",
|
||||
"seconds": "{ seconds, plural, 0 { second } 1 {1 second } other {# seconds } }",
|
||||
"short": {
|
||||
"days": "{ days, plural, 1 {1 day } other {# days } }",
|
||||
"hours": "{ hours, plural, 1 {1 hour } other {# hours } }",
|
||||
"minutes": "{{minutes}} min ",
|
||||
"seconds": "{{seconds}} sec "
|
||||
},
|
||||
"sec": "{{ sec }} sec",
|
||||
"realtime": "Realtime",
|
||||
"history": "History",
|
||||
"last-prefix": "last",
|
||||
@ -3416,7 +3424,8 @@
|
||||
"last": "Last",
|
||||
"time-period": "Time period",
|
||||
"hide": "Hide",
|
||||
"interval": "Interval"
|
||||
"interval": "Interval",
|
||||
"just-now": "Just now"
|
||||
},
|
||||
"user": {
|
||||
"user": "User",
|
||||
@ -4748,6 +4757,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"
|
||||
"allow-alarms-assign": "Allow alarms assignment"
|
||||
},
|
||||
"value-source": {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user