2019-08-29 12:08:49 +03:00
|
|
|
///
|
2023-01-31 10:43:56 +02:00
|
|
|
/// Copyright © 2016-2023 The Thingsboard Authors
|
2019-08-29 12:08:49 +03:00
|
|
|
///
|
|
|
|
|
/// 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 {
|
2023-02-16 14:51:24 +02:00
|
|
|
CellActionDescriptorType,
|
2019-08-29 12:08:49 +03:00
|
|
|
DateEntityTableColumn,
|
|
|
|
|
EntityTableColumn,
|
|
|
|
|
EntityTableConfig
|
|
|
|
|
} from '@home/models/entity/entities-table-config.models';
|
|
|
|
|
import { EntityType, EntityTypeResource, entityTypeTranslations } from '@shared/models/entity-type.models';
|
|
|
|
|
import { TranslateService } from '@ngx-translate/core';
|
|
|
|
|
import { DatePipe } from '@angular/common';
|
|
|
|
|
import { Direction } from '@shared/models/page/sort-order';
|
2020-02-10 13:15:29 +02:00
|
|
|
import { MatDialog } from '@angular/material/dialog';
|
2019-08-29 12:08:49 +03:00
|
|
|
import { TimePageLink } from '@shared/models/page/page-link';
|
|
|
|
|
import { Observable } from 'rxjs';
|
|
|
|
|
import { PageData } from '@shared/models/page/page-data';
|
|
|
|
|
import { EntityId } from '@shared/models/id/entity-id';
|
|
|
|
|
import {
|
|
|
|
|
AlarmInfo,
|
|
|
|
|
AlarmQuery,
|
|
|
|
|
AlarmSearchStatus,
|
|
|
|
|
alarmSeverityColors,
|
2023-02-14 15:33:35 +02:00
|
|
|
alarmSeverityTranslations,
|
|
|
|
|
AlarmsMode,
|
2019-08-29 12:08:49 +03:00
|
|
|
alarmStatusTranslations
|
|
|
|
|
} from '@app/shared/models/alarm.models';
|
|
|
|
|
import { AlarmService } from '@app/core/http/alarm.service';
|
|
|
|
|
import { DialogService } from '@core/services/dialog.service';
|
|
|
|
|
import { AlarmTableHeaderComponent } from '@home/components/alarm/alarm-table-header.component';
|
|
|
|
|
import {
|
|
|
|
|
AlarmDetailsDialogComponent,
|
|
|
|
|
AlarmDetailsDialogData
|
|
|
|
|
} from '@home/components/alarm/alarm-details-dialog.component';
|
2021-02-16 11:56:49 +02:00
|
|
|
import { DAY, historyInterval } from '@shared/models/time/time.models';
|
2022-06-21 16:05:10 +03:00
|
|
|
import { Store } from '@ngrx/store';
|
|
|
|
|
import { AppState } from '@core/core.state';
|
|
|
|
|
import { getCurrentAuthUser } from '@core/auth/auth.selectors';
|
|
|
|
|
import { Authority } from '@shared/models/authority.enum';
|
2023-02-16 14:51:24 +02:00
|
|
|
import { ChangeDetectorRef, Injector, StaticProvider, ViewContainerRef } from '@angular/core';
|
|
|
|
|
import { ConnectedPosition, Overlay, OverlayConfig, OverlayRef } from '@angular/cdk/overlay';
|
|
|
|
|
import {
|
|
|
|
|
ALARM_ASSIGNEE_PANEL_DATA, AlarmAssigneePanelComponent,
|
|
|
|
|
AlarmAssigneePanelData
|
|
|
|
|
} from '@home/components/alarm/alarm-assignee-panel.component';
|
|
|
|
|
import { ComponentPortal } from '@angular/cdk/portal';
|
|
|
|
|
import { isDefinedAndNotNull } from '@core/utils';
|
|
|
|
|
import { UtilsService } from '@core/services/utils.service';
|
2019-08-29 12:08:49 +03:00
|
|
|
|
|
|
|
|
export class AlarmTableConfig extends EntityTableConfig<AlarmInfo, TimePageLink> {
|
|
|
|
|
|
2022-06-21 16:05:10 +03:00
|
|
|
private authUser = getCurrentAuthUser(this.store);
|
|
|
|
|
|
2019-08-29 12:08:49 +03:00
|
|
|
searchStatus: AlarmSearchStatus;
|
|
|
|
|
|
|
|
|
|
constructor(private alarmService: AlarmService,
|
|
|
|
|
private dialogService: DialogService,
|
|
|
|
|
private translate: TranslateService,
|
|
|
|
|
private datePipe: DatePipe,
|
|
|
|
|
private dialog: MatDialog,
|
2023-02-13 19:09:05 +02:00
|
|
|
private alarmsMode: AlarmsMode = AlarmsMode.ALL,
|
2019-08-29 12:08:49 +03:00
|
|
|
public entityId: EntityId = null,
|
2022-06-21 16:05:10 +03:00
|
|
|
private defaultSearchStatus: AlarmSearchStatus = AlarmSearchStatus.ANY,
|
2023-02-13 19:09:05 +02:00
|
|
|
private store: Store<AppState>,
|
2023-02-16 14:51:24 +02:00
|
|
|
private viewContainerRef: ViewContainerRef,
|
|
|
|
|
private overlay: Overlay,
|
|
|
|
|
private cd: ChangeDetectorRef,
|
2023-03-10 12:56:00 +02:00
|
|
|
private utilsService: UtilsService,
|
2023-02-13 19:09:05 +02:00
|
|
|
pageMode = false) {
|
2019-08-29 12:08:49 +03:00
|
|
|
super();
|
|
|
|
|
this.loadDataOnInit = false;
|
|
|
|
|
this.tableTitle = '';
|
|
|
|
|
this.useTimePageLink = true;
|
2023-02-13 19:09:05 +02:00
|
|
|
this.pageMode = pageMode;
|
2021-02-16 11:56:49 +02:00
|
|
|
this.defaultTimewindowInterval = historyInterval(DAY * 30);
|
2019-08-29 12:08:49 +03:00
|
|
|
this.detailsPanelEnabled = false;
|
|
|
|
|
this.selectionEnabled = false;
|
|
|
|
|
this.searchEnabled = true;
|
|
|
|
|
this.addEnabled = false;
|
|
|
|
|
this.entitiesDeleteEnabled = false;
|
|
|
|
|
this.actionsColumnTitle = 'alarm.details';
|
|
|
|
|
this.entityType = EntityType.ALARM;
|
|
|
|
|
this.entityTranslations = entityTypeTranslations.get(EntityType.ALARM);
|
|
|
|
|
this.entityResources = {
|
2020-04-07 17:06:04 +03:00
|
|
|
} as EntityTypeResource<AlarmInfo>;
|
2019-08-29 12:08:49 +03:00
|
|
|
this.searchStatus = defaultSearchStatus;
|
|
|
|
|
|
|
|
|
|
this.headerComponent = AlarmTableHeaderComponent;
|
|
|
|
|
|
|
|
|
|
this.entitiesFetchFunction = pageLink => this.fetchAlarms(pageLink);
|
|
|
|
|
|
|
|
|
|
this.defaultSortOrder = {property: 'createdTime', direction: Direction.DESC};
|
|
|
|
|
|
|
|
|
|
this.columns.push(
|
|
|
|
|
new DateEntityTableColumn<AlarmInfo>('createdTime', 'alarm.created-time', this.datePipe, '150px'));
|
|
|
|
|
this.columns.push(
|
2020-01-24 19:05:41 +02:00
|
|
|
new EntityTableColumn<AlarmInfo>('originatorName', 'alarm.originator', '25%',
|
2019-08-29 12:08:49 +03:00
|
|
|
(entity) => entity.originatorName, entity => ({}), false));
|
|
|
|
|
this.columns.push(
|
2020-01-24 19:05:41 +02:00
|
|
|
new EntityTableColumn<AlarmInfo>('type', 'alarm.type', '25%'));
|
2019-08-29 12:08:49 +03:00
|
|
|
this.columns.push(
|
2020-01-24 19:05:41 +02:00
|
|
|
new EntityTableColumn<AlarmInfo>('severity', 'alarm.severity', '25%',
|
2019-08-29 12:08:49 +03:00
|
|
|
(entity) => this.translate.instant(alarmSeverityTranslations.get(entity.severity)),
|
2023-02-16 14:51:24 +02:00
|
|
|
entity => ({
|
|
|
|
|
fontWeight: 'bold',
|
|
|
|
|
color: alarmSeverityColors.get(entity.severity)
|
|
|
|
|
})));
|
|
|
|
|
this.columns.push(
|
2023-02-20 14:13:41 +02:00
|
|
|
new EntityTableColumn<AlarmInfo>('assignee', 'alarm.assignee', '200px',
|
2023-02-16 14:51:24 +02:00
|
|
|
(entity) => {
|
|
|
|
|
return this.getAssigneeTemplate(entity)
|
|
|
|
|
},
|
|
|
|
|
(entity) => {
|
|
|
|
|
return {
|
|
|
|
|
display: 'flex',
|
|
|
|
|
justifyContent: 'start',
|
|
|
|
|
alignItems: 'center',
|
|
|
|
|
height: 'inherit'
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
false,
|
|
|
|
|
() => ({}),
|
|
|
|
|
(entity) => undefined,
|
|
|
|
|
false,
|
|
|
|
|
{
|
|
|
|
|
icon: 'keyboard_arrow_down',
|
|
|
|
|
type: CellActionDescriptorType.DEFAULT,
|
|
|
|
|
isEnabled: (entity) => true,
|
|
|
|
|
name: this.translate.instant('alarm.assign'),
|
|
|
|
|
onAction: ($event, entity) => this.openAlarmAssigneePanel($event, entity)
|
|
|
|
|
})
|
|
|
|
|
)
|
2019-08-29 12:08:49 +03:00
|
|
|
this.columns.push(
|
2020-01-24 19:05:41 +02:00
|
|
|
new EntityTableColumn<AlarmInfo>('status', 'alarm.status', '25%',
|
2019-08-29 12:08:49 +03:00
|
|
|
(entity) => this.translate.instant(alarmStatusTranslations.get(entity.status))));
|
|
|
|
|
|
|
|
|
|
this.cellActionDescriptors.push(
|
|
|
|
|
{
|
|
|
|
|
name: this.translate.instant('alarm.details'),
|
|
|
|
|
icon: 'more_horiz',
|
2022-06-21 17:14:03 +03:00
|
|
|
isEnabled: () => true,
|
2019-08-29 12:08:49 +03:00
|
|
|
onAction: ($event, entity) => this.showAlarmDetails(entity)
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fetchAlarms(pageLink: TimePageLink): Observable<PageData<AlarmInfo>> {
|
2023-03-07 18:30:58 +02:00
|
|
|
const query = new AlarmQuery(this.entityId, pageLink, this.searchStatus, null, true, null);
|
2023-02-13 19:09:05 +02:00
|
|
|
switch (this.alarmsMode) {
|
|
|
|
|
case AlarmsMode.ALL:
|
|
|
|
|
return this.alarmService.getAllAlarms(query);
|
|
|
|
|
case AlarmsMode.ENTITY:
|
|
|
|
|
return this.alarmService.getAlarms(query);
|
|
|
|
|
}
|
2019-08-29 12:08:49 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
showAlarmDetails(entity: AlarmInfo) {
|
2022-06-21 17:14:03 +03:00
|
|
|
const isPermissionWrite = this.authUser.authority !== Authority.CUSTOMER_USER || entity.customerId.id === this.authUser.customerId;
|
2019-08-29 12:08:49 +03:00
|
|
|
this.dialog.open<AlarmDetailsDialogComponent, AlarmDetailsDialogData, boolean>
|
|
|
|
|
(AlarmDetailsDialogComponent,
|
|
|
|
|
{
|
|
|
|
|
disableClose: true,
|
|
|
|
|
panelClass: ['tb-dialog', 'tb-fullscreen-dialog'],
|
|
|
|
|
data: {
|
|
|
|
|
alarmId: entity.id.id,
|
2022-06-21 17:14:03 +03:00
|
|
|
alarm: entity,
|
|
|
|
|
allowAcknowledgment: isPermissionWrite,
|
|
|
|
|
allowClear: isPermissionWrite,
|
2019-08-29 12:08:49 +03:00
|
|
|
displayDetails: true
|
|
|
|
|
}
|
|
|
|
|
}).afterClosed().subscribe(
|
|
|
|
|
(res) => {
|
|
|
|
|
if (res) {
|
2022-01-19 17:02:18 +02:00
|
|
|
this.updateData();
|
2019-08-29 12:08:49 +03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
}
|
2023-02-16 14:51:24 +02:00
|
|
|
|
|
|
|
|
getAssigneeTemplate(entity: AlarmInfo): string {
|
|
|
|
|
return `
|
|
|
|
|
<span class="assignee-cell">
|
|
|
|
|
${isDefinedAndNotNull(entity.assigneeId) ?
|
|
|
|
|
`<span class="assigned-container">
|
|
|
|
|
<span class="user-avatar" style="background-color: ${this.getAvatarBgColor(entity)}">
|
|
|
|
|
${this.getUserInitials(entity)}
|
|
|
|
|
</span>
|
2023-02-20 14:13:41 +02:00
|
|
|
<span class="user-display-name">${this.getUserDisplayName(entity)}</span>
|
2023-02-16 14:51:24 +02:00
|
|
|
</span>`
|
|
|
|
|
:
|
|
|
|
|
`<mat-icon class="material-icons unassigned-icon">account_circle</mat-icon>
|
|
|
|
|
<span>${this.translate.instant('alarm.unassigned')}</span>`
|
|
|
|
|
}
|
|
|
|
|
</span>`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
getUserDisplayName(entity: AlarmInfo) {
|
|
|
|
|
let displayName = '';
|
2023-02-20 14:13:41 +02:00
|
|
|
if ((entity.assignee.firstName && entity.assignee.firstName.length > 0) ||
|
|
|
|
|
(entity.assignee.lastName && entity.assignee.lastName.length > 0)) {
|
|
|
|
|
if (entity.assignee.firstName) {
|
|
|
|
|
displayName += entity.assignee.firstName;
|
2023-02-16 14:51:24 +02:00
|
|
|
}
|
2023-02-20 14:13:41 +02:00
|
|
|
if (entity.assignee.lastName) {
|
2023-02-16 14:51:24 +02:00
|
|
|
if (displayName.length > 0) {
|
|
|
|
|
displayName += ' ';
|
|
|
|
|
}
|
2023-02-20 14:13:41 +02:00
|
|
|
displayName += entity.assignee.lastName;
|
2023-02-16 14:51:24 +02:00
|
|
|
}
|
|
|
|
|
} else {
|
2023-02-20 14:13:41 +02:00
|
|
|
displayName = entity.assignee.email;
|
2023-02-16 14:51:24 +02:00
|
|
|
}
|
|
|
|
|
return displayName;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
getUserInitials(entity: AlarmInfo): string {
|
|
|
|
|
let initials = '';
|
2023-02-20 14:13:41 +02:00
|
|
|
if (entity.assignee.firstName && entity.assignee.firstName.length ||
|
|
|
|
|
entity.assignee.lastName && entity.assignee.lastName.length) {
|
|
|
|
|
if (entity.assignee.firstName) {
|
|
|
|
|
initials += entity.assignee.firstName.charAt(0);
|
2023-02-16 14:51:24 +02:00
|
|
|
}
|
2023-02-20 14:13:41 +02:00
|
|
|
if (entity.assignee.lastName) {
|
|
|
|
|
initials += entity.assignee.lastName.charAt(0);
|
2023-02-16 14:51:24 +02:00
|
|
|
}
|
|
|
|
|
} else {
|
2023-02-20 14:13:41 +02:00
|
|
|
initials += entity.assignee.email.charAt(0);
|
2023-02-16 14:51:24 +02:00
|
|
|
}
|
|
|
|
|
return initials.toUpperCase();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
getAvatarBgColor(entity: AlarmInfo) {
|
|
|
|
|
return this.utilsService.stringToHslColor(this.getUserDisplayName(entity), 40, 60);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
openAlarmAssigneePanel($event: Event, entity: AlarmInfo) {
|
|
|
|
|
if ($event) {
|
|
|
|
|
$event.stopPropagation();
|
|
|
|
|
}
|
|
|
|
|
const target = $event.target || $event.srcElement || $event.currentTarget;
|
|
|
|
|
const config = new OverlayConfig();
|
|
|
|
|
config.backdropClass = 'cdk-overlay-transparent-backdrop';
|
|
|
|
|
config.hasBackdrop = true;
|
|
|
|
|
const connectedPosition: ConnectedPosition = {
|
|
|
|
|
originX: 'end',
|
|
|
|
|
originY: 'bottom',
|
2023-02-25 14:02:12 +02:00
|
|
|
overlayX: 'end',
|
2023-02-16 14:51:24 +02:00
|
|
|
overlayY: 'top'
|
|
|
|
|
};
|
|
|
|
|
config.positionStrategy = this.overlay.position().flexibleConnectedTo(target as HTMLElement)
|
|
|
|
|
.withPositions([connectedPosition]);
|
|
|
|
|
config.minWidth = '260px';
|
|
|
|
|
const overlayRef = this.overlay.create(config);
|
|
|
|
|
overlayRef.backdropClick().subscribe(() => {
|
|
|
|
|
overlayRef.dispose();
|
|
|
|
|
});
|
|
|
|
|
const providers: StaticProvider[] = [
|
|
|
|
|
{
|
|
|
|
|
provide: ALARM_ASSIGNEE_PANEL_DATA,
|
|
|
|
|
useValue: {
|
|
|
|
|
alarmId: entity.id.id,
|
|
|
|
|
assigneeId: entity.assigneeId?.id
|
|
|
|
|
} as AlarmAssigneePanelData
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
provide: OverlayRef,
|
|
|
|
|
useValue: overlayRef
|
|
|
|
|
}
|
|
|
|
|
];
|
|
|
|
|
const injector = Injector.create({parent: this.viewContainerRef.injector, providers});
|
|
|
|
|
overlayRef.attach(new ComponentPortal(AlarmAssigneePanelComponent,
|
|
|
|
|
this.viewContainerRef, injector)).onDestroy(() => this.updateData());
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-29 12:08:49 +03:00
|
|
|
}
|