@c8y/ngx-components
Version:
Angular modules for Cumulocity IoT applications
335 lines • 93.1 kB
JavaScript
import { Component, Input, Optional } from '@angular/core';
import { ALARM_STATUS_LABELS, AlarmStatus, AlarmService, AuditService, InventoryService, SEVERITY_LABELS } from '@c8y/client';
import { AlertService, AppStateService, ColorService, RelativeTimePipe, gettext, InterAppService, SupportedApps } from '@c8y/ngx-components';
import { TranslateService } from '@ngx-translate/core';
import { cloneDeep } from 'lodash-es';
import { AlarmDetailsService } from './alarm-details.service';
import { ALARM_DEFAULT_PROPERTIES, ALARM_STATUS_ICON } from './alarms.model';
import { Ng1SmartRulesUpgradeService } from './ng1-smart-rules-upgrade.service';
import { AlarmsViewService } from './alarms-view.service';
import { firstValueFrom } from 'rxjs';
import * as i0 from "@angular/core";
import * as i1 from "./alarm-details.service";
import * as i2 from "@c8y/client";
import * as i3 from "@c8y/ngx-components";
import * as i4 from "./ng1-smart-rules-upgrade.service";
import * as i5 from "@ngx-translate/core";
import * as i6 from "./alarms-view.service";
import * as i7 from "@angular/common";
import * as i8 from "ngx-bootstrap/popover";
import * as i9 from "@angular/router";
import * as i10 from "./alarm-details-custom-button/alarm-details-button.pipe";
import * as i11 from "./alarm-severity-to-icon.pipe";
import * as i12 from "./alarm-status-to-icon.pipe";
import * as i13 from "./audit-changes-message.pipe";
export class AlarmDetailsComponent {
constructor(alarmDetailsService, alarmService, alertService, appState, auditService, relativeTime, ng1SmartRulesUpgradeService, translateService, inventoryService, alarmsViewService, colorService, interAppService) {
this.alarmDetailsService = alarmDetailsService;
this.alarmService = alarmService;
this.alertService = alertService;
this.appState = appState;
this.auditService = auditService;
this.relativeTime = relativeTime;
this.ng1SmartRulesUpgradeService = ng1SmartRulesUpgradeService;
this.translateService = translateService;
this.inventoryService = inventoryService;
this.alarmsViewService = alarmsViewService;
this.colorService = colorService;
this.interAppService = interAppService;
this.ACKNOWLEDGED_STATUS_VALUE = AlarmStatus.ACKNOWLEDGED;
this.ACTIVE_STATUS_VALUE = AlarmStatus.ACTIVE;
this.CLEARED_STATUS_VALUE = AlarmStatus.CLEARED;
this.ACKNOWLEDGE_LABEL = gettext('Acknowledge');
this.REACTIVATE_LABEL = gettext('Reactivate');
this.SEVERITY_LABELS = SEVERITY_LABELS;
this.BELL_SLASH_ICON = ALARM_STATUS_ICON.BELL_SLASH;
this.BELL_ICON = ALARM_STATUS_ICON.BELL;
this.deviceManagementAppKey = SupportedApps.devicemanagement;
this.linkTitle = gettext('Open in {{ appName }}');
this.PAGE_SIZE = 100;
/**
* Indicates when alarms status change was started (Acknowledge/Reactivate)
*/
this.isAlarmStatusChanging = false;
/**
* Custom fragments of the selected alarm. If none exist, null is returned.
*/
this.customFragments = null;
}
async ngOnInit() {
const isSmartRulesServiceSubscribed = !!(await firstValueFrom(this.interAppService.getApp$(SupportedApps.smartrules)));
const hasAnyRoleAllowingToCreateSmartRule = this.alarmDetailsService.checkIfHasAnyRoleAllowingToCreateSmartRule();
this.isCreateSmartRulesButtonAvailable =
!!this.ng1SmartRulesUpgradeService &&
isSmartRulesServiceSubscribed &&
hasAnyRoleAllowingToCreateSmartRule;
this.userDeviceManagementApp$ = this.interAppService.getApp$(this.deviceManagementAppKey);
this.showSourceNavigationLink$ = this.interAppService.shouldShowAppLink$(this.deviceManagementAppKey);
this.typeColor = await this.colorService.generateColor(this.selectedAlarm.type);
}
async ngOnChanges(changes) {
if (changes.selectedAlarm && changes.selectedAlarm.currentValue) {
await this.reloadAuditLog(true, true);
await this.updateStatusMessage();
const { data } = await this.inventoryService.detail(this.selectedAlarm.source.id);
this.selectedAlarmMO = data;
this.customFragments = this.getCustomFragments(this.selectedAlarm);
}
}
createSmartRule() {
if (!this.isCreateSmartRulesButtonAvailable) {
return;
}
this.ng1SmartRulesUpgradeService.addNewForInputAlarmAndOutputUserWithUI(this.selectedAlarm, this.appState.currentUser.value);
}
/**
* Navigates to a specific alarm source device based on the provided source.
*
* @param sourceId - The source id.
*/
async goToAlarmSource(sourceId) {
const { data } = await this.alarmService.detail(sourceId);
await this.interAppService.navigateToApp(this.deviceManagementAppKey, `#/device/${data.source.id}/alarms`);
}
/**
* Reloads audit log data asynchronously.
*
* This method fetches audit records using `getAlarmAuditRecords` and optionally updates the audit logs
* state in the component based on the `isSetAuditLogs` flag. It handles the loading state and potential
* errors during the fetch operation.
*
* @param isRevert - A boolean flag indicating whether to retrieve a 100 (see PAGE_SIZE) records (true)
* or only record, that chronologically will be the oldest one (false). Defaults to true.
* If set to false, it will set PAGE_SIZE to 1 and trigger a logic
* concatenating a most recent record with the very first one to
* calculate the alarm duration (change to CLEARED status).
* It's passed to the `getAlarmAuditRecords` method.
* @param isSetAuditLogs - A boolean flag to determine if the fetched audit logs should be set in the component state. Defaults to `false`.
* @returns A promise that resolves to a list of `IAuditRecord` objects.
*/
async reloadAuditLog(isRevert = true, isSetAuditLogs = false) {
try {
this.isLoading = true;
const auditLogs = await this.getAlarmAuditRecords(isRevert);
if (isSetAuditLogs) {
this.setAuditLogs(auditLogs);
}
return auditLogs;
}
catch (error) {
this.alertService.addServerFailure(error);
}
finally {
this.isLoading = false;
}
}
async onUpdateDetails(status) {
try {
this.isAlarmStatusChanging = true;
await this.updateAlarmStatus(status);
await this.reloadAuditLog(true, true);
await this.updateStatusMessage();
this.updateLastUpdatedDate(this.auditLog.data[0]);
}
catch (error) {
this.alertService.addServerFailure(error);
}
finally {
this.isAlarmStatusChanging = false;
}
}
async detailsButtonAction(button, alarm) {
const result = button.action(alarm);
let shouldReload = false;
if (result instanceof Promise) {
shouldReload = await result;
}
else {
shouldReload = result;
}
if (shouldReload) {
let alarm;
if (shouldReload === true) {
const { data: updatedAlarm } = await this.alarmService.detail(this.selectedAlarm.id);
alarm = updatedAlarm;
}
else {
alarm = shouldReload;
}
this.alarmsViewService.updateAlarmList();
const previousValue = this.selectedAlarm;
this.selectedAlarm = alarm;
this.ngOnChanges({
selectedAlarm: {
currentValue: alarm,
previousValue,
firstChange: false,
isFirstChange: () => false
}
});
}
}
async updateAlarmStatus(status) {
const partiallyUpdatedAlarm = { id: this.selectedAlarm.id, status };
await this.alarmService.update(partiallyUpdatedAlarm);
const translatedStatusLabel = this.translateService.instant(ALARM_STATUS_LABELS[status]);
this.alertService.success(this.translateService.instant(gettext('Alarm status changed to {{ status }}'), {
status: translatedStatusLabel.toUpperCase()
}));
this.selectedAlarm.status = status;
this.alarmsViewService.updateAlarmList();
}
/**
* Retrieves the audit log and appends the last audit record to it.
*
* This method fetches the existing audit log data and makes a deep copy of it. It then
* retrieves the last audit record and appends it to the copied audit log data. This is
* useful for scenarios where the most recent audit record needs to be included in the
* existing audit log data (calculating the CLEARED period).
*
* @returns A promise of `IResultList<IAuditRecord>`, which includes the
* existing audit log data along with the last audit record appended.
* @private
*/
async auditLogWithFirstRecord() {
const existingData = this.auditLog;
const copiedExistingData = cloneDeep(existingData);
const lastAuditRecord = await this.reloadAuditLog(false);
const lastRecord = lastAuditRecord.data[lastAuditRecord.data.length - 1];
copiedExistingData.data.push(lastRecord);
return copiedExistingData;
}
setAuditLogs(auditLogs) {
this.auditLog = auditLogs;
}
updateLastUpdatedDate(updatedAuditRecords) {
if (!updatedAuditRecords) {
return;
}
const { creationTime } = updatedAuditRecords;
this.selectedAlarm.lastUpdated = creationTime;
}
getActiveStatusMessage(time) {
return this.translateService.instant(gettext('ACTIVE`alarm`: triggered {{alarmTimeFromNow}}'), {
alarmTimeFromNow: this.relativeTime.transform(new Date(time))
});
}
getAcknowledgedStatusMessage(status, changeLog) {
if (changeLog.length === 0) {
return this.translateService.instant(gettext('ACKNOWLEDGED`alarm`'));
}
const acknowledgedBy = this.alarmDetailsService.getAcknowledgedBy(status, changeLog);
const acknowledgeTime = this.alarmDetailsService.getAcknowledgeTime(changeLog);
if (acknowledgedBy) {
return this.translateService.instant(gettext('ACKNOWLEDGED`alarm` by: {{ackBy}} {{ackTimeFromNow}}'), {
ackBy: acknowledgedBy,
ackTimeFromNow: this.relativeTime.transform(new Date(acknowledgeTime))
});
}
return this.translateService.instant(gettext('ACKNOWLEDGED`alarm` {{ackTimeFromNow}}'), {
ackTimeFromNow: this.relativeTime.transform(new Date(acknowledgeTime))
});
}
getClearedStatusMessage(auditLog) {
if (auditLog.length === 0) {
return this.translateService.instant(gettext('CLEARED`alarm`'));
}
const differenceInMs = this.calculateAlarmDuration(auditLog);
return this.translateService.instant(gettext('CLEARED`alarm`: was active for {{alarmDuration}}'), {
alarmDuration: this.relativeTime.transform(differenceInMs, true)
});
}
/**
* Calculates the duration of an alarm based on audit log records.
*
* This method computes the duration of an alarm by finding the difference
* between the start and end times of the alarm. The start time is determined
* from the last record in the audit log, using the first available time field
* (`firstOccurrenceTime`, `time`, or `creationTime`). The end time is obtained
* from the `alarmDetailsService`.
*
* @param auditLog - An array of `IAuditRecord` objects representing the audit log records.
* @returns The duration of the alarm in milliseconds, or `null` if the end time is not available.
* @private
*/
calculateAlarmDuration(auditLog) {
const firstAlarm = auditLog[auditLog.length - 1];
const startTime = firstAlarm.firstOccurrenceTime || firstAlarm.time || firstAlarm.creationTime;
const endTime = this.alarmDetailsService.getEndTime(auditLog);
if (!endTime) {
return null;
}
const startTimeToDate = new Date(startTime);
const endTimeToDate = new Date(endTime);
return endTimeToDate.getTime() - startTimeToDate.getTime();
}
/**
* Retrieves a list of audit records for a selected alarm.
*
* This method fetches audit records based on the specified properties, including
* the date, page size, whether to revert, the source alarm ID, and whether to include total pages.
*
* @param isRevert - A boolean flag indicating whether to retrieve a 100 (see PAGE_SIZE) records (true)
* or only record, that chronologically will be the oldest one (false). Defaults to true.
* If set to false, it will set PAGE_SIZE to 1 and trigger a logic
* concatenating a most recent record with the very first one to
* calculate the alarm duration (change to CLEARED status).
* @returns A Promise that resolves to an IResultList of IAuditRecord objects, representing the audit records.
* @async
* @private
*/
async getAlarmAuditRecords(isRevert = true) {
const properties = {
dateTo: new Date(Date.now()).toISOString(),
pageSize: isRevert ? this.PAGE_SIZE : 1,
revert: isRevert,
source: this.selectedAlarm.id,
withTotalPages: true
};
return await this.auditService.list(properties);
}
async updateStatusMessage() {
switch (this.selectedAlarm.status) {
case this.ACTIVE_STATUS_VALUE:
this.statusMessage = this.getActiveStatusMessage(this.selectedAlarm.time);
break;
case this.ACKNOWLEDGED_STATUS_VALUE:
this.statusMessage = this.getAcknowledgedStatusMessage(this.selectedAlarm.status, this.auditLog.data);
break;
case this.CLEARED_STATUS_VALUE:
if (this.hasReachedOrExceededPageSizeLimit()) {
this.extendedAuditLogs = await this.auditLogWithFirstRecord();
this.statusMessage = this.getClearedStatusMessage(this.extendedAuditLogs.data);
return;
}
this.statusMessage = this.getClearedStatusMessage(this.auditLog.data);
break;
}
}
hasReachedOrExceededPageSizeLimit() {
return this.auditLog.data.length >= this.PAGE_SIZE;
}
getCustomFragments(selectedAlarm) {
let customProperties = null;
for (const key in selectedAlarm) {
if (!ALARM_DEFAULT_PROPERTIES.find(k => k === key)) {
if (!customProperties) {
customProperties = {};
}
customProperties[key] = selectedAlarm[key];
}
}
return customProperties;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: AlarmDetailsComponent, deps: [{ token: i1.AlarmDetailsService }, { token: i2.AlarmService }, { token: i3.AlertService }, { token: i3.AppStateService }, { token: i2.AuditService }, { token: i3.RelativeTimePipe }, { token: i4.Ng1SmartRulesUpgradeService, optional: true }, { token: i5.TranslateService }, { token: i2.InventoryService }, { token: i6.AlarmsViewService }, { token: i3.ColorService }, { token: i3.InterAppService }], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: AlarmDetailsComponent, selector: "c8y-alarm-details", inputs: { selectedAlarm: "selectedAlarm" }, usesOnChanges: true, ngImport: i0, template: "<div class=\"d-flex row tight-grid flex-wrap a-i-stretch\">\n <div class=\"col-xs-12 col-md-6 d-flex p-b-8\">\n <div\n class=\"border-all fit-w d-flex\"\n data-cy=\"c8y-alarm-details--status-section-wrapper\"\n >\n <div\n class=\"p-8\"\n data-cy=\"c8y-alarm-details--status-icon\"\n >\n <i\n class=\"icon-24 text-gray-dark m-t-4 c8y-icon\"\n [c8yIcon]=\"selectedAlarm.status | AlarmStatusToIcon\"\n ></i>\n </div>\n <div class=\"p-t-8 p-b-8 p-r-8\">\n <p class=\"text-label-small m-b-0 m-r-8\">{{ 'Status' | translate }}</p>\n <p class=\"small\">{{ statusMessage }}</p>\n </div>\n </div>\n </div>\n <div class=\"col-xs-12 col-md-6 d-flex p-b-8\">\n <div\n class=\"border-all fit-w d-flex\"\n data-cy=\"c8y-alarm-details--severity-section-wrapper\"\n >\n <div class=\"p-8\">\n <i\n class=\"icon-24 text-gray-dark m-t-4 stroked-icon status\"\n [c8yIcon]=\"selectedAlarm.severity | AlarmSeverityToIcon\"\n [ngClass]=\"selectedAlarm.severity?.toString() | lowercase\"\n ></i>\n </div>\n <div class=\"p-t-8 p-b-8 p-r-8\">\n <p class=\"text-label-small m-b-0 m-r-8\">{{ 'Severity' | translate }}</p>\n <p class=\"small\">{{ SEVERITY_LABELS[selectedAlarm.severity] | translate }}</p>\n </div>\n </div>\n </div>\n <div\n class=\"col-xs-12 col-md-6 d-flex p-b-8\"\n data-cy=\"c8y-alarm-details--source-wrapper\"\n >\n <div class=\"border-all fit-w d-flex\">\n <div class=\"p-8\">\n <i\n class=\"icon-24 text-gray-dark m-t-4 stroked-icon status\"\n c8yIcon=\"contactless-payment\"\n ></i>\n </div>\n <div class=\"p-t-8 p-b-8 p-r-8\">\n <p class=\"text-label-small m-b-0 m-r-8\">{{ 'Source' | translate }}</p>\n <p class=\"small\">\n <button\n class=\"btn-link text-muted p-0 m-r-8 text-left\"\n title=\"{{ selectedAlarm.source.name }}\"\n type=\"button\"\n routerLink=\"{{ selectedAlarmMO | assetLink }}\"\n >\n <small class=\"icon-flex\">\n <i c8yIcon=\"exchange\"></i>\n {{ selectedAlarm.source.name || selectedAlarm.source.id }}\n </small>\n </button>\n <ng-container *ngIf=\"showSourceNavigationLink$ | async\">\n <button\n class=\"btn-link p-0 text-left\"\n title=\"{{\n linkTitle\n | translate\n : { appName: userDeviceManagementApp$ | async | humanizeAppName | async }\n }}\"\n type=\"button\"\n (click)=\"goToAlarmSource(selectedAlarm.id)\"\n data-cy=\"alarm-details-device-management-link\"\n >\n {{ userDeviceManagementApp$ | async | humanizeAppName | async }}\n <i c8yIcon=\"external-link\"></i>\n </button>\n </ng-container>\n </p>\n </div>\n </div>\n </div>\n <div\n class=\"col-xs-12 col-md-6 d-flex p-b-8\"\n data-cy=\"c8y-alarm-details--severity-type-wrapper\"\n >\n <div class=\"border-all fit-w d-flex\">\n <div class=\"p-8\">\n <span\n class=\"circle-icon-wrapper\"\n [ngStyle]=\"{ 'background-color': typeColor }\"\n >\n <i\n class=\"stroked-icon\"\n c8yIcon=\"bell\"\n ></i>\n </span>\n </div>\n <div class=\"p-t-8 p-b-8 p-r-8 min-width-0\">\n <p class=\"text-label-small m-b-0 m-r-8\">{{ 'Type' | translate }}</p>\n <p\n class=\"small text-truncate\"\n title=\"{{ selectedAlarm.type }}\"\n >\n <code>{{ selectedAlarm.type }}</code>\n </p>\n </div>\n </div>\n </div>\n\n <div class=\"col-xs-12 col-md-12 p-b-16\">\n <div class=\"border-all fit-w d-flex\">\n <div class=\"p-8\">\n <i\n class=\"icon-24 text-gray-dark m-t-4\"\n data-cy=\"c8y-alarm-details--last-updated-icon\"\n c8yIcon=\"calendar\"\n data-cy=\"c8y-alarm-details--last-updated-icon\"\n ></i>\n </div>\n <div class=\"p-t-8 p-b-0 p-r-8 flex-grow\">\n <div class=\"content-flex-50\">\n <div\n class=\"col-4 p-b-8\"\n *ngIf=\"selectedAlarm.count > 1\"\n data-cy=\"c8y-alarm-details--number-of-occurrences-wrapper\"\n >\n <p class=\"text-label-small m-b-0 m-r-8\">{{ 'Number of occurrences' | translate }}</p>\n <p>\n <span data-cy=\"c8y-alarm-details--badge\" class=\"badge badge-info\">{{ selectedAlarm.count }}</span>\n </p>\n </div>\n <div\n class=\"col-4 p-b-8\"\n *ngIf=\"selectedAlarm.count > 1\"\n data-cy=\"c8y-alarm-details--first-occurrence-wrapper\"\n >\n <p class=\"text-label-small m-b-0 m-r-8\">{{ 'First occurrence' | translate }}</p>\n <p class=\"small\">\n {{ selectedAlarm.creationTime | c8yDate: 'medium' }}\n\n <button\n class=\"btn-help btn-help--sm\"\n [attr.aria-label]=\"'Help' | translate\"\n popover=\"{{\n 'Time in which the alarm was created. The time shown corresponds to the server\\'s time. Device time can be different from server time.'\n | translate\n }}\"\n placement=\"right\"\n triggers=\"focus\"\n container=\"body\"\n type=\"button\"\n ></button>\n </p>\n </div>\n <div\n class=\"col-4 p-b-8\"\n data-cy=\"c8y-alarm-details--last-updated-wrapper\"\n >\n <p class=\"text-label-small m-b-0 m-r-8\">{{ 'Last occurrence' | translate }}</p>\n <p class=\"small\">\n {{ selectedAlarm.lastUpdated | c8yDate: 'medium' }}\n\n <button\n class=\"btn-help btn-help--sm\"\n [attr.aria-label]=\"'Help' | translate\"\n popover=\"{{\n 'Time in which the alarm was last updated. The time shown corresponds to the server\\'s time. Device time can be different from server time.'\n | translate\n }}\"\n placement=\"right\"\n triggers=\"focus\"\n container=\"body\"\n type=\"button\"\n ></button>\n </p>\n </div>\n </div>\n </div>\n </div>\n </div>\n\n <div\n class=\"col-xs-12 col-md-12 p-b-16\"\n data-cy=\"c8y-alarm-details--custom-fragments-wrapper\"\n *ngIf=\"customFragments\"\n >\n <div class=\"border-all fit-w d-flex\">\n <div class=\"p-8\">\n <i\n class=\"icon-24 text-gray-dark m-t-4\"\n c8yIcon=\"outgoing-data\"\n ></i>\n </div>\n <div\n class=\"p-t-8 p-b-0 p-r-8 flex-grow\"\n data-cy=\"alarm-details-custom-data\"\n >\n <p class=\"text-label-small m-b-4 m-r-8\">{{ 'Custom data' | translate }}</p>\n <pre><code>{{ customFragments | json }}</code></pre>\n </div>\n </div>\n </div>\n</div>\n\n<div class=\"d-flex flex-wrap gap-8\">\n <button\n class=\"btn btn-default btn-sm\"\n [title]=\"'Reload audit logs' | translate\"\n type=\"submit\"\n (click)=\"reloadAuditLog(true, true)\"\n data-cy=\"c8y-alarms-details--reload-audit-logs\"\n >\n <i\n c8yIcon=\"refresh\"\n [ngClass]=\"{ 'icon-spin': isLoading }\"\n ></i>\n {{ 'Reload audit logs' | translate }}\n </button>\n\n <button\n class=\"btn btn-default btn-sm\"\n [title]=\"\n selectedAlarm.status !== ACKNOWLEDGED_STATUS_VALUE\n ? (ACKNOWLEDGE_LABEL | translate)\n : (REACTIVATE_LABEL | translate)\n \"\n type=\"submit\"\n (click)=\"\n onUpdateDetails(\n selectedAlarm.status !== ACKNOWLEDGED_STATUS_VALUE\n ? ACKNOWLEDGED_STATUS_VALUE\n : ACTIVE_STATUS_VALUE\n )\n \"\n [disabled]=\"selectedAlarm.status === CLEARED_STATUS_VALUE || isAlarmStatusChanging\"\n >\n <i\n [c8yIcon]=\"selectedAlarm.status !== ACKNOWLEDGED_STATUS_VALUE ? BELL_SLASH_ICON : BELL_ICON\"\n ></i>\n {{\n selectedAlarm.status !== ACKNOWLEDGED_STATUS_VALUE\n ? (ACKNOWLEDGE_LABEL | translate)\n : (REACTIVATE_LABEL | translate)\n }}\n </button>\n\n <button\n class=\"btn btn-default btn-sm\"\n [title]=\"'Create smart rule' | translate\"\n type=\"submit\"\n *ngIf=\"isCreateSmartRulesButtonAvailable\"\n (click)=\"createSmartRule()\"\n data-cy=\"c8y-alarms-details--create-smart-rule\"\n >\n <i c8yIcon=\"c8y-icon c8y-icon-smart-rules\"></i>\n {{ 'Create smart rule' | translate }}\n </button>\n <button\n class=\"btn btn-default btn-sm\"\n [title]=\"'Clear`alarm`' | translate\"\n type=\"submit\"\n data-cy=\"c8y-alarm-details--clear-alarm\"\n (click)=\"onUpdateDetails(CLEARED_STATUS_VALUE)\"\n [disabled]=\"selectedAlarm.status === CLEARED_STATUS_VALUE\"\n >\n <i c8yIcon=\"c8y-alert-idle\"></i>\n {{ 'Clear`alarm`' | translate }}\n </button>\n\n <button\n class=\"btn btn-default btn-sm\"\n [title]=\"button.title | translate\"\n type=\"button\"\n *ngFor=\"let button of selectedAlarm | alarmDetailsButton: selectedAlarmMO | async\"\n [ngClass]=\"button.additionalButtonClasses\"\n (click)=\"detailsButtonAction(button, selectedAlarm)\"\n [disabled]=\"button.disabled\"\n >\n <i\n [c8yIcon]=\"button.icon\"\n [ngClass]=\"button.additionalIconClasses\"\n ></i>\n <span *ngIf=\"button.label\">{{ button.label | translate }}</span>\n </button>\n</div>\n\n<ng-template #noAuditLogAvailable>\n <div class=\"p-16\">\n <c8y-ui-empty-state\n [icon]=\"'archive'\"\n [title]=\"'No audit logs found.' | translate\"\n [horizontal]=\"true\"\n ></c8y-ui-empty-state>\n </div>\n</ng-template>\n\n<div class=\"legend form-block\">{{ 'Audit logs' | translate }}</div>\n\n<ng-container *ngIf=\"isLoading || auditLog?.data.length; else noAuditLogAvailable\">\n <c8y-loading *ngIf=\"isLoading\"></c8y-loading>\n\n <c8y-list-group\n data-cy=\"c8y-alarms-details--audit-logs\"\n *ngIf=\"!isLoading\"\n >\n <c8y-li-timeline *c8yFor=\"let log of auditLog; loadMore: 'hidden'\">\n {{ log.creationTime | date: 'mediumDate' }}\n {{ log.creationTime | date: 'mediumTime' }}\n <c8y-li>\n <c8y-li-body>\n <p class=\"text-truncate-wrap separator-bottom p-b-4\">\n {{ log | auditChangesMessage }}\n </p>\n <div class=\"c8y-list__item__footer\">\n <span\n class=\"m-r-16 small\"\n *ngIf=\"log.user\"\n >\n <span class=\"text-label-small\">\n {{ 'by`user`' | translate }}\n </span>\n {{ log.user }}\n </span>\n <span class=\"small\">\n <span class=\"text-label-small\">\n {{ 'device time' | translate }}\n </span>\n {{ log.time | c8yDate: 'medium' }}\n </span>\n </div>\n </c8y-li-body>\n </c8y-li>\n </c8y-li-timeline>\n </c8y-list-group>\n</ng-container>\n", dependencies: [{ kind: "component", type: i3.EmptyStateComponent, selector: "c8y-ui-empty-state", inputs: ["icon", "title", "subtitle", "horizontal"] }, { kind: "directive", type: i3.IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "directive", type: i7.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i7.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i7.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i7.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: i3.ForOfDirective, selector: "[c8yFor]", inputs: ["c8yForOf", "c8yForLoadMore", "c8yForPipe", "c8yForNotFound", "c8yForMaxIterations", "c8yForLoadingTemplate", "c8yForLoadNextLabel", "c8yForLoadingLabel", "c8yForRealtime", "c8yForRealtimeOptions", "c8yForComparator", "c8yForEnableVirtualScroll", "c8yForVirtualScrollElementSize", "c8yForVirtualScrollStrategy", "c8yForVirtualScrollContainerHeight"], outputs: ["c8yForCount", "c8yForChange", "c8yForLoadMoreComponent"] }, { kind: "component", type: i3.LoadingComponent, selector: "c8y-loading", inputs: ["layout", "progress", "message"] }, { kind: "component", type: i3.ListGroupComponent, selector: "c8y-list-group" }, { kind: "component", type: i3.ListItemComponent, selector: "c8y-list-item, c8y-li", inputs: ["active", "highlighted", "emptyActions", "dense", "collapsed", "selectable"], outputs: ["collapsedChange"] }, { kind: "component", type: i3.ListItemBodyComponent, selector: "c8y-list-item-body, c8y-li-body", inputs: ["body"] }, { kind: "component", type: i3.ListItemTimelineComponent, selector: "c8y-list-item-timeline, c8y-li-timeline" }, { kind: "directive", type: i8.PopoverDirective, selector: "[popover]", inputs: ["adaptivePosition", "boundariesElement", "popover", "popoverContext", "popoverTitle", "placement", "outsideClick", "triggers", "container", "containerClass", "isOpen", "delay"], outputs: ["onShown", "onHidden"], exportAs: ["bs-popover"] }, { kind: "directive", type: i9.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "pipe", type: i7.AsyncPipe, name: "async" }, { kind: "pipe", type: i7.LowerCasePipe, name: "lowercase" }, { kind: "pipe", type: i7.JsonPipe, name: "json" }, { kind: "pipe", type: i7.DatePipe, name: "date" }, { kind: "pipe", type: i3.HumanizeAppNamePipe, name: "humanizeAppName" }, { kind: "pipe", type: i3.DatePipe, name: "c8yDate" }, { kind: "pipe", type: i3.AssetLinkPipe, name: "assetLink" }, { kind: "pipe", type: i5.TranslatePipe, name: "translate" }, { kind: "pipe", type: i10.AlarmDetailsButtonPipe, name: "alarmDetailsButton" }, { kind: "pipe", type: i11.AlarmSeverityToIconPipe, name: "AlarmSeverityToIcon" }, { kind: "pipe", type: i12.AlarmStatusToIconPipe, name: "AlarmStatusToIcon" }, { kind: "pipe", type: i13.AuditChangesMessagePipe, name: "auditChangesMessage" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: AlarmDetailsComponent, decorators: [{
type: Component,
args: [{ selector: 'c8y-alarm-details', template: "<div class=\"d-flex row tight-grid flex-wrap a-i-stretch\">\n <div class=\"col-xs-12 col-md-6 d-flex p-b-8\">\n <div\n class=\"border-all fit-w d-flex\"\n data-cy=\"c8y-alarm-details--status-section-wrapper\"\n >\n <div\n class=\"p-8\"\n data-cy=\"c8y-alarm-details--status-icon\"\n >\n <i\n class=\"icon-24 text-gray-dark m-t-4 c8y-icon\"\n [c8yIcon]=\"selectedAlarm.status | AlarmStatusToIcon\"\n ></i>\n </div>\n <div class=\"p-t-8 p-b-8 p-r-8\">\n <p class=\"text-label-small m-b-0 m-r-8\">{{ 'Status' | translate }}</p>\n <p class=\"small\">{{ statusMessage }}</p>\n </div>\n </div>\n </div>\n <div class=\"col-xs-12 col-md-6 d-flex p-b-8\">\n <div\n class=\"border-all fit-w d-flex\"\n data-cy=\"c8y-alarm-details--severity-section-wrapper\"\n >\n <div class=\"p-8\">\n <i\n class=\"icon-24 text-gray-dark m-t-4 stroked-icon status\"\n [c8yIcon]=\"selectedAlarm.severity | AlarmSeverityToIcon\"\n [ngClass]=\"selectedAlarm.severity?.toString() | lowercase\"\n ></i>\n </div>\n <div class=\"p-t-8 p-b-8 p-r-8\">\n <p class=\"text-label-small m-b-0 m-r-8\">{{ 'Severity' | translate }}</p>\n <p class=\"small\">{{ SEVERITY_LABELS[selectedAlarm.severity] | translate }}</p>\n </div>\n </div>\n </div>\n <div\n class=\"col-xs-12 col-md-6 d-flex p-b-8\"\n data-cy=\"c8y-alarm-details--source-wrapper\"\n >\n <div class=\"border-all fit-w d-flex\">\n <div class=\"p-8\">\n <i\n class=\"icon-24 text-gray-dark m-t-4 stroked-icon status\"\n c8yIcon=\"contactless-payment\"\n ></i>\n </div>\n <div class=\"p-t-8 p-b-8 p-r-8\">\n <p class=\"text-label-small m-b-0 m-r-8\">{{ 'Source' | translate }}</p>\n <p class=\"small\">\n <button\n class=\"btn-link text-muted p-0 m-r-8 text-left\"\n title=\"{{ selectedAlarm.source.name }}\"\n type=\"button\"\n routerLink=\"{{ selectedAlarmMO | assetLink }}\"\n >\n <small class=\"icon-flex\">\n <i c8yIcon=\"exchange\"></i>\n {{ selectedAlarm.source.name || selectedAlarm.source.id }}\n </small>\n </button>\n <ng-container *ngIf=\"showSourceNavigationLink$ | async\">\n <button\n class=\"btn-link p-0 text-left\"\n title=\"{{\n linkTitle\n | translate\n : { appName: userDeviceManagementApp$ | async | humanizeAppName | async }\n }}\"\n type=\"button\"\n (click)=\"goToAlarmSource(selectedAlarm.id)\"\n data-cy=\"alarm-details-device-management-link\"\n >\n {{ userDeviceManagementApp$ | async | humanizeAppName | async }}\n <i c8yIcon=\"external-link\"></i>\n </button>\n </ng-container>\n </p>\n </div>\n </div>\n </div>\n <div\n class=\"col-xs-12 col-md-6 d-flex p-b-8\"\n data-cy=\"c8y-alarm-details--severity-type-wrapper\"\n >\n <div class=\"border-all fit-w d-flex\">\n <div class=\"p-8\">\n <span\n class=\"circle-icon-wrapper\"\n [ngStyle]=\"{ 'background-color': typeColor }\"\n >\n <i\n class=\"stroked-icon\"\n c8yIcon=\"bell\"\n ></i>\n </span>\n </div>\n <div class=\"p-t-8 p-b-8 p-r-8 min-width-0\">\n <p class=\"text-label-small m-b-0 m-r-8\">{{ 'Type' | translate }}</p>\n <p\n class=\"small text-truncate\"\n title=\"{{ selectedAlarm.type }}\"\n >\n <code>{{ selectedAlarm.type }}</code>\n </p>\n </div>\n </div>\n </div>\n\n <div class=\"col-xs-12 col-md-12 p-b-16\">\n <div class=\"border-all fit-w d-flex\">\n <div class=\"p-8\">\n <i\n class=\"icon-24 text-gray-dark m-t-4\"\n data-cy=\"c8y-alarm-details--last-updated-icon\"\n c8yIcon=\"calendar\"\n data-cy=\"c8y-alarm-details--last-updated-icon\"\n ></i>\n </div>\n <div class=\"p-t-8 p-b-0 p-r-8 flex-grow\">\n <div class=\"content-flex-50\">\n <div\n class=\"col-4 p-b-8\"\n *ngIf=\"selectedAlarm.count > 1\"\n data-cy=\"c8y-alarm-details--number-of-occurrences-wrapper\"\n >\n <p class=\"text-label-small m-b-0 m-r-8\">{{ 'Number of occurrences' | translate }}</p>\n <p>\n <span data-cy=\"c8y-alarm-details--badge\" class=\"badge badge-info\">{{ selectedAlarm.count }}</span>\n </p>\n </div>\n <div\n class=\"col-4 p-b-8\"\n *ngIf=\"selectedAlarm.count > 1\"\n data-cy=\"c8y-alarm-details--first-occurrence-wrapper\"\n >\n <p class=\"text-label-small m-b-0 m-r-8\">{{ 'First occurrence' | translate }}</p>\n <p class=\"small\">\n {{ selectedAlarm.creationTime | c8yDate: 'medium' }}\n\n <button\n class=\"btn-help btn-help--sm\"\n [attr.aria-label]=\"'Help' | translate\"\n popover=\"{{\n 'Time in which the alarm was created. The time shown corresponds to the server\\'s time. Device time can be different from server time.'\n | translate\n }}\"\n placement=\"right\"\n triggers=\"focus\"\n container=\"body\"\n type=\"button\"\n ></button>\n </p>\n </div>\n <div\n class=\"col-4 p-b-8\"\n data-cy=\"c8y-alarm-details--last-updated-wrapper\"\n >\n <p class=\"text-label-small m-b-0 m-r-8\">{{ 'Last occurrence' | translate }}</p>\n <p class=\"small\">\n {{ selectedAlarm.lastUpdated | c8yDate: 'medium' }}\n\n <button\n class=\"btn-help btn-help--sm\"\n [attr.aria-label]=\"'Help' | translate\"\n popover=\"{{\n 'Time in which the alarm was last updated. The time shown corresponds to the server\\'s time. Device time can be different from server time.'\n | translate\n }}\"\n placement=\"right\"\n triggers=\"focus\"\n container=\"body\"\n type=\"button\"\n ></button>\n </p>\n </div>\n </div>\n </div>\n </div>\n </div>\n\n <div\n class=\"col-xs-12 col-md-12 p-b-16\"\n data-cy=\"c8y-alarm-details--custom-fragments-wrapper\"\n *ngIf=\"customFragments\"\n >\n <div class=\"border-all fit-w d-flex\">\n <div class=\"p-8\">\n <i\n class=\"icon-24 text-gray-dark m-t-4\"\n c8yIcon=\"outgoing-data\"\n ></i>\n </div>\n <div\n class=\"p-t-8 p-b-0 p-r-8 flex-grow\"\n data-cy=\"alarm-details-custom-data\"\n >\n <p class=\"text-label-small m-b-4 m-r-8\">{{ 'Custom data' | translate }}</p>\n <pre><code>{{ customFragments | json }}</code></pre>\n </div>\n </div>\n </div>\n</div>\n\n<div class=\"d-flex flex-wrap gap-8\">\n <button\n class=\"btn btn-default btn-sm\"\n [title]=\"'Reload audit logs' | translate\"\n type=\"submit\"\n (click)=\"reloadAuditLog(true, true)\"\n data-cy=\"c8y-alarms-details--reload-audit-logs\"\n >\n <i\n c8yIcon=\"refresh\"\n [ngClass]=\"{ 'icon-spin': isLoading }\"\n ></i>\n {{ 'Reload audit logs' | translate }}\n </button>\n\n <button\n class=\"btn btn-default btn-sm\"\n [title]=\"\n selectedAlarm.status !== ACKNOWLEDGED_STATUS_VALUE\n ? (ACKNOWLEDGE_LABEL | translate)\n : (REACTIVATE_LABEL | translate)\n \"\n type=\"submit\"\n (click)=\"\n onUpdateDetails(\n selectedAlarm.status !== ACKNOWLEDGED_STATUS_VALUE\n ? ACKNOWLEDGED_STATUS_VALUE\n : ACTIVE_STATUS_VALUE\n )\n \"\n [disabled]=\"selectedAlarm.status === CLEARED_STATUS_VALUE || isAlarmStatusChanging\"\n >\n <i\n [c8yIcon]=\"selectedAlarm.status !== ACKNOWLEDGED_STATUS_VALUE ? BELL_SLASH_ICON : BELL_ICON\"\n ></i>\n {{\n selectedAlarm.status !== ACKNOWLEDGED_STATUS_VALUE\n ? (ACKNOWLEDGE_LABEL | translate)\n : (REACTIVATE_LABEL | translate)\n }}\n </button>\n\n <button\n class=\"btn btn-default btn-sm\"\n [title]=\"'Create smart rule' | translate\"\n type=\"submit\"\n *ngIf=\"isCreateSmartRulesButtonAvailable\"\n (click)=\"createSmartRule()\"\n data-cy=\"c8y-alarms-details--create-smart-rule\"\n >\n <i c8yIcon=\"c8y-icon c8y-icon-smart-rules\"></i>\n {{ 'Create smart rule' | translate }}\n </button>\n <button\n class=\"btn btn-default btn-sm\"\n [title]=\"'Clear`alarm`' | translate\"\n type=\"submit\"\n data-cy=\"c8y-alarm-details--clear-alarm\"\n (click)=\"onUpdateDetails(CLEARED_STATUS_VALUE)\"\n [disabled]=\"selectedAlarm.status === CLEARED_STATUS_VALUE\"\n >\n <i c8yIcon=\"c8y-alert-idle\"></i>\n {{ 'Clear`alarm`' | translate }}\n </button>\n\n <button\n class=\"btn btn-default btn-sm\"\n [title]=\"button.title | translate\"\n type=\"button\"\n *ngFor=\"let button of selectedAlarm | alarmDetailsButton: selectedAlarmMO | async\"\n [ngClass]=\"button.additionalButtonClasses\"\n (click)=\"detailsButtonAction(button, selectedAlarm)\"\n [disabled]=\"button.disabled\"\n >\n <i\n [c8yIcon]=\"button.icon\"\n [ngClass]=\"button.additionalIconClasses\"\n ></i>\n <span *ngIf=\"button.label\">{{ button.label | translate }}</span>\n </button>\n</div>\n\n<ng-template #noAuditLogAvailable>\n <div class=\"p-16\">\n <c8y-ui-empty-state\n [icon]=\"'archive'\"\n [title]=\"'No audit logs found.' | translate\"\n [horizontal]=\"true\"\n ></c8y-ui-empty-state>\n </div>\n</ng-template>\n\n<div class=\"legend form-block\">{{ 'Audit logs' | translate }}</div>\n\n<ng-container *ngIf=\"isLoading || auditLog?.data.length; else noAuditLogAvailable\">\n <c8y-loading *ngIf=\"isLoading\"></c8y-loading>\n\n <c8y-list-group\n data-cy=\"c8y-alarms-details--audit-logs\"\n *ngIf=\"!isLoading\"\n >\n <c8y-li-timeline *c8yFor=\"let log of auditLog; loadMore: 'hidden'\">\n {{ log.creationTime | date: 'mediumDate' }}\n {{ log.creationTime | date: 'mediumTime' }}\n <c8y-li>\n <c8y-li-body>\n <p class=\"text-truncate-wrap separator-bottom p-b-4\">\n {{ log | auditChangesMessage }}\n </p>\n <div class=\"c8y-list__item__footer\">\n <span\n class=\"m-r-16 small\"\n *ngIf=\"log.user\"\n >\n <span class=\"text-label-small\">\n {{ 'by`user`' | translate }}\n </span>\n {{ log.user }}\n </span>\n <span class=\"small\">\n <span class=\"text-label-small\">\n {{ 'device time' | translate }}\n </span>\n {{ log.time | c8yDate: 'medium' }}\n </span>\n </div>\n </c8y-li-body>\n </c8y-li>\n </c8y-li-timeline>\n </c8y-list-group>\n</ng-container>\n" }]
}], ctorParameters: () => [{ type: i1.AlarmDetailsService }, { type: i2.AlarmService }, { type: i3.AlertService }, { type: i3.AppStateService }, { type: i2.AuditService }, { type: i3.RelativeTimePipe }, { type: i4.Ng1SmartRulesUpgradeService, decorators: [{
type: Optional
}] }, { type: i5.TranslateService }, { type: i2.InventoryService }, { type: i6.AlarmsViewService }, { type: i3.ColorService }, { type: i3.InterAppService }], propDecorators: { selectedAlarm: [{
type: Input
}] } });
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYWxhcm0tZGV0YWlscy5jb21wb25lbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9hbGFybXMvYWxhcm0tZGV0YWlscy5jb21wb25lbnQudHMiLCIuLi8uLi8uLi9hbGFybXMvYWxhcm0tZGV0YWlscy5jb21wb25lbnQuaHRtbCJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsU0FBUyxFQUFFLEtBQUssRUFBYSxRQUFRLEVBQWlCLE1BQU0sZUFBZSxDQUFDO0FBQ3JGLE9BQU8sRUFDTCxtQkFBbUIsRUFDbkIsV0FBVyxFQUNYLFlBQVksRUFFWixZQUFZLEVBTVosZ0JBQWdCLEVBQ2hCLGVBQWUsRUFDaEIsTUFBTSxhQUFhLENBQUM7QUFDckIsT0FBTyxFQUNMLFlBQVksRUFDWixlQUFlLEVBQ2YsWUFBWSxFQUNaLGdCQUFnQixFQUNoQixPQUFPLEVBRVAsZUFBZSxFQUNmLGFBQWEsRUFDZCxNQUFNLHFCQUFxQixDQUFDO0FBQzdCLE9BQU8sRUFBRSxnQkFBZ0IsRUFBRSxNQUFNLHFCQUFxQixDQUFDO0FBQ3ZELE9BQU8sRUFBRSxTQUFTLEVBQUUsTUFBTSxXQUFXLENBQUM7QUFDdEMsT0FBTyxFQUFFLG1CQUFtQixFQUFFLE1BQU0seUJBQXlCLENBQUM7QUFDOUQsT0FBTyxFQUNMLHdCQUF3QixFQUN4QixpQkFBaUIsRUFHbEIsTUFBTSxnQkFBZ0IsQ0FBQztBQUN4QixPQUFPLEVBQUUsMkJBQTJCLEVBQUUsTUFBTSxtQ0FBbUMsQ0FBQztBQUNoRixPQUFPLEVBQUUsaUJBQWlCLEVBQUUsTUFBTSx1QkFBdUIsQ0FBQztBQUMxRCxPQUFPLEVBQUUsY0FBYyxFQUFjLE1BQU0sTUFBTSxDQUFDOzs7Ozs7Ozs7Ozs7Ozs7QUFNbEQsTUFBTSxPQUFPLHFCQUFxQjtJQW1FaEMsWUFDVSxtQkFBd0MsRUFDeEMsWUFBMEIsRUFDMUIsWUFBMEIsRUFDMUIsUUFBeUIsRUFDekIsWUFBMEIsRUFDMUIsWUFBOEIsRUFDbEIs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