UNPKG

@c8y/ngx-components

Version:

Angular modules for Cumulocity IoT applications

335 lines 86.1 kB
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 "@angular/router"; import * as i9 from "./alarm-details-custom-button/alarm-details-button.pipe"; import * as i10 from "./alarm-severity-to-icon.pipe"; import * as i11 from "./alarm-status-to-icon.pipe"; import * as i12 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.updateLastUpdatedDate(this.auditLog.data[0]); 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.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 class=\"border-all fit-w d-flex\">\n <div class=\"p-8\">\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 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]=\"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 class=\"col-xs-12 col-md-6 d-flex p-b-8\">\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: { 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 class=\"col-xs-12 col-md-6 d-flex p-b-8\">\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 c8yIcon=\"calendar\"\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 class=\"col-4 p-b-8\">\n <p class=\"text-label-small m-b-0 m-r-8\">{{ 'Last updated' | translate }}</p>\n <p class=\"small\">\n {{ lastUpdated | c8yDate: 'medium' }}\n </p>\n </div>\n <div\n class=\"col-4 p-b-8\"\n *ngIf=\"selectedAlarm.count > 1\"\n >\n <p class=\"text-label-small m-b-0 m-r-8\">{{ 'Number of occurrences' | translate }}</p>\n <p>\n <span 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 >\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 </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 *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 >\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 >\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 (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 *ngFor=\"let button of selectedAlarm | alarmDetailsButton: selectedAlarmMO | async\"\n class=\"btn btn-default btn-sm\"\n [ngClass]=\"button.additionalButtonClasses\"\n [title]=\"button.title | translate\"\n type=\"button\"\n (click)=\"detailsButtonAction(button, selectedAlarm)\"\n [disabled]=\"button.disabled\"\n >\n <i [ngClass]=\"button.additionalIconClasses\" [c8yIcon]=\"button.icon\"></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.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: i9.AlarmDetailsButtonPipe, name: "alarmDetailsButton" }, { kind: "pipe", type: i10.AlarmSeverityToIconPipe, name: "AlarmSeverityToIcon" }, { kind: "pipe", type: i11.AlarmStatusToIconPipe, name: "AlarmStatusToIcon" }, { kind: "pipe", type: i12.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 class=\"border-all fit-w d-flex\">\n <div class=\"p-8\">\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 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]=\"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 class=\"col-xs-12 col-md-6 d-flex p-b-8\">\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: { 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 class=\"col-xs-12 col-md-6 d-flex p-b-8\">\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 c8yIcon=\"calendar\"\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 class=\"col-4 p-b-8\">\n <p class=\"text-label-small m-b-0 m-r-8\">{{ 'Last updated' | translate }}</p>\n <p class=\"small\">\n {{ lastUpdated | c8yDate: 'medium' }}\n </p>\n </div>\n <div\n class=\"col-4 p-b-8\"\n *ngIf=\"selectedAlarm.count > 1\"\n >\n <p class=\"text-label-small m-b-0 m-r-8\">{{ 'Number of occurrences' | translate }}</p>\n <p>\n <span 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 >\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 </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 *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 >\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 >\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 (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 *ngFor=\"let button of selectedAlarm | alarmDetailsButton: selectedAlarmMO | async\"\n class=\"btn btn-default btn-sm\"\n [ngClass]=\"button.additionalButtonClasses\"\n [title]=\"button.title | translate\"\n type=\"button\"\n (click)=\"detailsButtonAction(button, selectedAlarm)\"\n [disabled]=\"button.disabled\"\n >\n <i [ngClass]=\"button.additionalIconClasses\" [c8yIcon]=\"button.icon\"></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,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYWxhcm0tZGV0YWlscy5jb21wb25lbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9hbGFybXMvYWxhcm0tZGV0YWlscy5jb21wb25lbnQudHMiLCIuLi8uLi8uLi9hbGFybXMvYWxhcm0tZGV0YWlscy5jb21wb25lbnQuaHRtbCJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsU0FBUyxFQUFFLEtBQUssRUFBYSxRQUFRLEVBQWlCLE1BQU0sZUFBZSxDQUFDO0FBQ3JGLE9BQU8sRUFDTCxtQkFBbUIsRUFDbkIsV0FBVyxFQUNYLFlBQVksRUFFWixZQUFZLEVBTVosZ0JBQWdCLEVBQ2hCLGVBQWUsRUFDaEIsTUFBTSxhQUFhLENBQUM7QUFDckIsT0FBTyxFQUNMLFlBQVksRUFDWixlQUFlLEVBQ2YsWUFBWSxFQUNaLGdCQUFnQixFQUNoQixPQUFPLEVBRVAsZUFBZSxFQUNmLGFBQWEsRUFDZCxNQUFNLHFCQUFxQixDQUFDO0FBQzdCLE9BQU8sRUFBRSxnQkFBZ0IsRUFBRSxNQUFNLHFCQUFxQixDQUFDO0FBQ3ZELE9BQU8sRUFBRSxTQUFTLEVBQUUsTUFBTSxXQUFXLENBQUM7QUFDdEMsT0FBTyxFQUFFLG1CQUFtQixFQUFFLE1BQU0seUJBQXlCLENBQUM7QUFDOUQsT0FBTyxFQUNMLHdCQUF3QixFQUN4QixpQkFBaUIsRUFHbEIsTUFBTSxnQkFBZ0IsQ0FBQztBQUN4QixPQUFPLEVBQUUsMkJBQTJCLEVBQUUsTUFBTSxtQ0FBbUMsQ0FBQztBQUNoRixPQUFPLEVBQUUsaUJBQWlCLEVBQUUsTUFBTSx1QkFBdUIsQ0FBQztBQUMxRCxPQUFPLEVBQUUsY0FBYyxFQUFjLE1BQU0sTUFBTSxDQUFDOzs7Ozs7Ozs7Ozs7OztBQU1sRCxNQUFNLE9BQU8scUJBQXFCO0lBdUVoQyxZQUNVLG1CQUF3QyxFQUN4QyxZQUEwQixFQUMxQixZQUEwQixFQUMxQixRQUF5QixFQUN6QixZQUEwQixFQUMxQixZQUE4QixFQUNsQiwy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