@c8y/ngx-components
Version:
Angular modules for Cumulocity IoT applications
2,635 lines • 244 kB
JavaScript
import * as i0 from '@angular/core';
import { Injectable, Pipe, InjectionToken, HostListener, Input, Optional, Component, EventEmitter, ViewChild, Output, forwardRef, signal, NgModule } from '@angular/core';
import { combineLatest, Subject, BehaviorSubject, fromEvent, firstValueFrom, of, from, forkJoin, EMPTY, Observable, pipe, take, takeUntil as takeUntil$1, map as map$1 } from 'rxjs';
import { filter, map, switchMap, startWith, debounceTime, takeUntil, distinctUntilChanged, catchError, finalize, tap, shareReplay, throttleTime } from 'rxjs/operators';
import * as i3 from '@c8y/ngx-components';
import { Permissions, gettext, ViewContext, SupportedApps, IconDirective, ProductExperienceDirective, EmptyStateComponent, LoadingComponent, ListGroupComponent, ForOfDirective, ListItemTimelineComponent, ListItemComponent, ListItemBodyComponent, C8yTranslatePipe, DatePipe, HumanizeAppNamePipe, AssetLinkPipe, TitleComponent, TabsOutletComponent, RequiredInputPlaceholderDirective, CountdownIntervalComponent, DynamicComponentAlertAggregator, DynamicComponentAlert, C8yTranslateDirective, ListItemIconComponent, GuideDocsComponent, GuideHrefDirective, DynamicComponentAlertsComponent, DropdownDirectionDirective, FormGroupComponent, DateTimePickerComponent, MessagesComponent, MessageDirective, ListItemCheckboxComponent, ActionBarItemComponent, HelpComponent, AlarmWithChildrenRealtimeService, RouterTabsResolver, ContextRouteGuard, ContextRouteComponent, hookNavigator, hookRoute, CommonModule, CoreModule, HeaderModule, C8yTranslateModule, DynamicComponentModule, RelativeTimePipe } from '@c8y/ngx-components';
import { sortBy, cloneDeep } from 'lodash-es';
import * as i2 from '@c8y/client';
import { AlarmStatus, Severity, ALARM_STATUS_LABELS, SEVERITY_LABELS } from '@c8y/client';
import * as i1$1 from '@ngx-translate/core';
import { INTERVAL_TITLES, INTERVALS, IntervalPickerComponent } from '@c8y/ngx-components/interval-picker';
import * as i1 from '@angular/router';
import { RouterLink, NavigationEnd, RouterLinkActive, RouterOutlet, RouterModule } from '@angular/router';
import { NgClass, NgIf, NgStyle, NgFor, AsyncPipe, LowerCasePipe, JsonPipe, TitleCasePipe } from '@angular/common';
import { PopoverDirective, PopoverModule } from 'ngx-bootstrap/popover';
import * as i1$2 from '@angular/forms';
import { FormsModule, ReactiveFormsModule, NG_VALUE_ACCESSOR } from '@angular/forms';
import { BsDropdownDirective, BsDropdownToggleDirective, BsDropdownMenuDirective, BsDropdownModule } from 'ngx-bootstrap/dropdown';
import { CdkTrapFocus, A11yModule } from '@angular/cdk/a11y';
import * as i1$4 from 'ngx-bootstrap/tooltip';
import { TooltipDirective, TooltipModule } from 'ngx-bootstrap/tooltip';
import * as i1$3 from '@c8y/ngx-components/alarm-event-selector';
import { AlarmEventSelectorModule } from '@c8y/ngx-components/alarm-event-selector';
/**
* A service to retrieve custom buttons for the alarm details view.
*/
class AlarmDetailsButtonService {
constructor(serviceRegistry, pluginsResolver) {
this.serviceRegistry = serviceRegistry;
this.pluginsResolver = pluginsResolver;
}
get$(alarm, source) {
const providers$ = this.pluginsResolver.allPluginsLoaded$.pipe(filter(Boolean), map(() => {
return this.serviceRegistry.get('alarmDetailsButton');
}));
return providers$.pipe(switchMap(providers => {
const observables$ = providers.map(provider => provider.getAlarmDetailsButton$(alarm, source).pipe(startWith(false)));
return combineLatest(observables$);
}), map(indicators => {
return indicators.filter(Boolean);
}), map(indicators => sortBy(indicators, this.byPriority)));
}
byPriority(item) {
if (item.priority === undefined) {
return 0;
}
return -item.priority;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AlarmDetailsButtonService, deps: [{ token: i3.ServiceRegistry }, { token: i3.PluginsResolveService }], target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AlarmDetailsButtonService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AlarmDetailsButtonService, decorators: [{
type: Injectable,
args: [{ providedIn: 'root' }]
}], ctorParameters: () => [{ type: i3.ServiceRegistry }, { type: i3.PluginsResolveService }] });
/**
* A pipe to provide custom buttons for the alarm details view.
*
* Will call `get$()` method of `AlarmDetailsButtonService` to get the custom buttons for the provided alarm.
*/
class AlarmDetailsButtonPipe {
constructor(alarmDetailsButtonService) {
this.alarmDetailsButtonService = alarmDetailsButtonService;
}
transform(alarm, source) {
return this.alarmDetailsButtonService.get$(alarm, source);
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AlarmDetailsButtonPipe, deps: [{ token: AlarmDetailsButtonService }], target: i0.ɵɵFactoryTarget.Pipe }); }
static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "19.2.14", ngImport: i0, type: AlarmDetailsButtonPipe, isStandalone: true, name: "alarmDetailsButton" }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AlarmDetailsButtonPipe, decorators: [{
type: Pipe,
args: [{
standalone: true,
name: 'alarmDetailsButton',
pure: true
}]
}], ctorParameters: () => [{ type: AlarmDetailsButtonService }] });
class AlarmDetailsService {
constructor(permissions) {
this.permissions = permissions;
this.STATUS_ATTRIBUTE = 'status';
}
/**
* Retrieves the username of the user who acknowledged an alarm status.
*
* This method checks if the provided status is equal to the acknowledged
* status. If it is not, or if the
* audit log is empty or the first log item does not contain a user, the
* method returns a default value ('--').
*
* If the status is the acknowledged status and the audit log contains valid
* records, the method iterates over the audit records in reverse order
* (starting from the most recent). It finds the first record where the
* status attribute (defined by this.STATUS_ATTRIBUTE) has been changed to
* the acknowledged status. The method then returns the username of the user
* who made this change.
*
* If no such change is found in the audit records, it returns the username
* from the first record of the audit log.
*
* There can be multiple audit logs with ACKNOWLEDGED status.
*
* @param status - The current status of the alarm.
* @param auditLog - An array of audit records to process.
* @returns The username of the user who acknowledged the status
* or '--' if the status is not acknowledged or audit log is invalid.
*/
getAcknowledgedBy(status, auditLog) {
let acknowledgedBy = '--';
if (status !== AlarmStatus.ACKNOWLEDGED || !auditLog || !auditLog[0]?.user) {
return acknowledgedBy;
}
acknowledgedBy = auditLog[0].user;
return auditLog.reduceRight((acc, auditLogItem) => {
const changes = Array.from(auditLogItem.changes || []);
const acknowledgedStatusChange = changes.find((change) => change.attribute === this.STATUS_ATTRIBUTE && change.newValue === AlarmStatus.ACKNOWLEDGED);
return (acknowledgedStatusChange && auditLogItem.user) || acc;
}, acknowledgedBy);
}
/**
* Calculates the acknowledge time from a list of audit records.
*
* This method iterates over the provided audit records in reverse order
* (starting from the most recent) and finds the first record where a
* specific status attribute (defined by this.STATUS_ATTRIBUTE) has been
* acknowledged. It then returns the creation time of that record.
*
* If no such record is found, the method returns the creation time of the
* first audit record. If the audit record list is empty, it returns null.
*
* There can be multiple audit logs with ACKNOWLEDGED status.
*
* @param auditLog - An array of audit records to process.
* @returns The creation time of the acknowledged record,
* the creation time of the first record if no acknowledged record is found,
* or null if the audit log is empty.
*/
getAcknowledgeTime(auditLog) {
const initialValue = auditLog.length ? auditLog[0].creationTime : null;
return auditLog.reduceRight((acc, auditLogItem) => {
const changes = Array.from(auditLogItem.changes || []);
const acknowledgedStatusChange = changes.find((change) => change.attribute === this.STATUS_ATTRIBUTE && change.newValue === AlarmStatus.ACKNOWLEDGED);
return acknowledgedStatusChange ? auditLogItem.creationTime : acc;
}, initialValue);
}
/**
* Retrieves the end time of an event from an audit log.
*
* The method processes the provided audit log to find the first instance
* (starting from the most recent record) where the status was changed to 'CLEARED'.
* It iterates over the audit records and
* checks the changes in each record to find this status change.
*
* If a record with the CLEARED status is found, the method returns the creation time
* of that record. If the entire audit log is processed without finding a CLEARED status,
* the creation time of the first audit log record is returned.
*
* If the audit log is empty or null, the method returns null.
*
* There can be only one audit log with CLEARED status.
*
* @param auditLog - An array of audit records to process.
* @returns The creation time of the record with the CLEARED status,
* the creation time of the first record if no CLEARED status is found,
* or null if the audit log is empty or null.
*/
getEndTime(auditLog) {
if (!auditLog || auditLog.length === 0) {
return null;
}
let latestClearedAuditTime = null;
for (const auditLogItem of auditLog) {
const changes = Array.from(auditLogItem.changes || []);
const clearedStatusChange = changes.find(change => change.attribute === this.STATUS_ATTRIBUTE && change.newValue === AlarmStatus.CLEARED);
if (clearedStatusChange) {
if (!latestClearedAuditTime || auditLogItem.creationTime > latestClearedAuditTime) {
latestClearedAuditTime = auditLogItem.creationTime;
}
}
}
return latestClearedAuditTime || auditLog[0].creationTime;
}
checkIfHasAnyRoleAllowingToCreateSmartRule() {
const ROLES_ALLOWING_SMART_RULE_CREATION = [
[
Permissions.ROLE_INVENTORY_ADMIN,
Permissions.ROLE_INVENTORY_CREATE,
Permissions.ROLE_MANAGED_OBJECT_ADMIN,
Permissions.ROLE_MANAGED_OBJECT_CREATE
],
[Permissions.ROLE_CEP_MANAGEMENT_ADMIN, Permissions.ROLE_SMARTRULE_ADMIN]
];
return (this.permissions.hasAnyRole(ROLES_ALLOWING_SMART_RULE_CREATION[0]) &&
this.permissions.hasAnyRole(ROLES_ALLOWING_SMART_RULE_CREATION[1]));
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AlarmDetailsService, deps: [{ token: i3.Permissions }], target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AlarmDetailsService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AlarmDetailsService, decorators: [{
type: Injectable,
args: [{
providedIn: 'root'
}]
}], ctorParameters: () => [{ type: i3.Permissions }] });
const ALARMS_MODULE_CONFIG = new InjectionToken('AlarmsModuleConfig');
const ALARM_STATUS_ICON = {
ALERT_IDLE: 'c8y-alert-idle',
BELL_SLASH: 'bell-slash',
BELL: 'bell'
};
/**
* A lookup table to map alarm statuses to corresponding icons.
*/
const AlarmIconMap = {
[AlarmStatus.CLEARED]: ALARM_STATUS_ICON.ALERT_IDLE,
[AlarmStatus.ACKNOWLEDGED]: ALARM_STATUS_ICON.BELL_SLASH,
[AlarmStatus.ACTIVE]: ALARM_STATUS_ICON.BELL
};
const ALARM_SEVERITY_ICON = {
CIRCLE: 'circle',
HIGH_PRIORITY: 'high-priority',
WARNING: 'warning',
EXCLAMATION_CIRCLE: 'exclamation-circle'
};
const HELP_ICON = 'help';
/**
* A lookup table to map alarm severity types to corresponding icons.
*/
const ALARM_SEVERITY_ICON_MAP = {
[Severity.CRITICAL]: ALARM_SEVERITY_ICON.EXCLAMATION_CIRCLE,
[Severity.MAJOR]: ALARM_SEVERITY_ICON.WARNING,
[Severity.MINOR]: ALARM_SEVERITY_ICON.HIGH_PRIORITY,
[Severity.WARNING]: ALARM_SEVERITY_ICON.CIRCLE
};
/**
* Extended interval titles with an additional title for the case when no date is selected.
*/
const INTERVAL_TITLES_EXTENDED = {
...INTERVAL_TITLES,
none: gettext('No date filter')
};
const INTERVALS_EXTENDED = [
{
id: 'none',
title: gettext('No date filter')
},
...INTERVALS
];
const DEFAULT_ALARM_COUNTS = { CRITICAL: 0, MAJOR: 0, MINOR: 0, WARNING: 0 };
const DEFAULT_SEVERITY_VALUES = {
[Severity.CRITICAL]: true,
[Severity.MAJOR]: true,
[Severity.MINOR]: true,
[Severity.WARNING]: true
};
const DEFAULT_STATUS_VALUES = {
[AlarmStatus.ACTIVE]: true,
[AlarmStatus.ACKNOWLEDGED]: true,
[AlarmStatus.CLEARED]: true
};
const ALARMS_PATH = 'alarms';
/**
* Default properties of a alarm. Used to extract the custom properties from a Alarm object.
*/
const ALARM_DEFAULT_PROPERTIES = [
'severity',
'source',
'type',
'time',
'text',
'id',
'status',
'count',
'name',
'history',
'self',
'creationTime',
'firstOccurrenceTime',
'lastUpdated'
];
const THROTTLE_REALTIME_REFRESH = 1_000;
const PRODUCT_EXPERIENCE_ALARMS = {
EVENTS: {
ALARMS: 'Alarms'
},
COMPONENTS: {
ALARMS_FILTER: 'alarms-filter',
ALARMS_INTERVAL_REFRESH: 'alarms-interval-refresh',
ALARMS: 'alarms',
ALARMS_TYPE_FILTER: 'alarms-type-filter',
ALARM_DETAILS: 'alarm-details'
},
ACTIONS: {
APPLY_FILTER: 'applyFilter',
REMOVE_CHIP_FILTER: 'removeChipFilter',
APPLY_TYPE_FILTER: 'applyTypeFilter',
CREATE_SMART_RULE: 'createSmartRule',
ACKNOWLEDGE_ALARM: 'acknowledgeAlarm',
REACTIVATE_ALARM: 'reactivateAlarm',
CLEAR_ALARM: 'clearAlarm',
RELOAD_AUDIT_LOGS: 'reloadAuditLogs',
USER_SPEND_TIME_ON_COMPONENT: 'userSpendTimeOnComponent'
}
};
/**
* This service is a duplicate of smart-rules-service with slight name change.
* Duplicating allows to pass 'Verify App tutorial' job.
* Name renames allows to pass 'Reusable build codex' job.
* Overall this service is considered as a workaround.
* In ticket MTM-58985 we will investigate if it's possible to remove this service
* along with making failing jobs pass.
*/
class Ng1SmartRulesUpgradeService {
}
function SmartRulesUpgradeServiceFactory(injector) {
return injector.get('smartRulesSvc');
}
const smartRulesUpgradeServiceProvider = {
provide: Ng1SmartRulesUpgradeService,
useFactory: SmartRulesUpgradeServiceFactory,
deps: ['$injector']
};
/**
* Service for managing and retrieving alarms data within the alarms view.
*
* The `AlarmsViewService` provides functionality to interact with alarms,
* including filtering, counting, and translation-related operations in an alarms view.
*
* This service relies on the `AlarmService` for fetching alarm data and the `OptionsService`
* for configuring alarms view options.
*/
class AlarmsViewService {
constructor(alarmService, optionsService, router, contextRouteService) {
this.alarmService = alarmService;
this.optionsService = optionsService;
this.router = router;
this.contextRouteService = contextRouteService;
this.ALARM_REFRESH_TYPE_KEY = 'alarmsRefreshType';
this.DEFAULT_INTERVAL_VALUE = 30_000;
this.DEFAULT_REFRESH_OPTION_VALUE = 'interval';
this.DEFAULT_INTERVAL_VALUES = [5_000, 10_000, 15_000, 30_000, 60_000];
this.REALTIME_UPDATE_ALARMS_MESSAGE = gettext('The list was updated, click to refresh.');
this.reloadAlarmsList$ = new Subject();
this.closeDetailsView$ = new Subject();
if (this.isIntervalRefresh()) {
this._isIntervalEnabled = new Subject();
this.isIntervalEnabled$ = this._isIntervalEnabled.asObservable();
}
}
/**
* Emits a subject to initialize the alarms reloading.
*/
updateAlarmList(value = null) {
this.reloadAlarmsList$.next(value);
}
/**
* Retrieves a list of alarms filtered by specified severities and other optional query filters.
*
* @param severities an array of severities to filter the alarms.
* @param showCleared flag indicating whether to show cleared alarms. Defaults to false.
* @param selectedDates an array of two dates to filter alarms by creation and last update dates.
* @param filter additional query filters for retrieving alarms.
*
* @returns A promise that resolves to a list of alarms satisfying the specified filters.
*/
retrieveFilteredAlarms(severities, showCleared = false, selectedDates, filter) {
const severitiesQuery = this.getSeverityQueryParameter(severities);
const statusesQuery = this.getStatusQueryParameter(showCleared);
const _filter = {
pageSize: 50,
withTotalPages: true,
...(severitiesQuery && { severity: severitiesQuery }),
...(statusesQuery && { status: statusesQuery }),
...(selectedDates && {
lastUpdatedFrom: selectedDates[0].toISOString(),
createdTo: selectedDates[1].toISOString()
}),
...filter
};
return this.alarmService.list(_filter);
}
retrieveAlarmsByDate(dates) {
return this.alarmService.list({
lastUpdatedFrom: dates[0].toISOString(),
createdTo: dates[1].toISOString(),
pageSize: 50,
withTotalPages: true
});
}
/**
* Updates the state to enable or disable intervals.
* @param value - A boolean value to indicate whether to enable intervals.
*/
updateIntervalState(value) {
this._isIntervalEnabled?.next(value);
}
/**
* Fetches the count of alarms filtered by severity and clearance status.
*
* @param severity - The severity level to filter by (e.g., CRITICAL, MAJOR, etc.).
* @param showCleared - Whether or not to include cleared alarms in the count.
* @param filter - Additional filter criteria for alarms.
*
* @returns A promise that resolves to the number of alarms that match the filter criteria.
*
*/
async getAlarmsCountBySeverity(severity, showCleared, filter) {
const statusesQuery = this.getStatusQueryParameter(showCleared);
const _filter = {
...(severity && { severity: severity }),
...(statusesQuery && { status: statusesQuery }),
...filter
};
const { data } = await this.alarmService.count(_filter);
return data;
}
/**
* Retrieves the current alarms refresh type from the OptionsService
* and determines whether it is set to "interval".
*
* @returns `true` if the alarms refresh type is "interval," otherwise `false`.
*/
isIntervalRefresh() {
const value = this.optionsService.get(this.ALARM_REFRESH_TYPE_KEY, 'interval');
return value === 'interval';
}
/**
* Updates the list of selected severities based on the new severity filter.
*
* @param severityUpdates - The object representing the updates to each severity.
*
* @returns An array representing the updated selected severities.
*/
updateSelectedSeverities(severityUpdates) {
return Object.keys(severityUpdates)
.filter(key => severityUpdates[key])
.map(key => key.toUpperCase());
}
/**
* Clears all active alarms of the selected severities.
*
* This method clears all active alarms for the given list of severities by making bulk update calls. If no severities are selected, it defaults to using all available severities.
* It works by sending a series of update requests for each severity and returns a Promise that resolves with an object indicating if all alarms were resolved immediately.
*
* @param selectedSeverities An array of severities to be cleared. If not provided, all severities will be cleared.
* @param sourceId - Identifier for the source associated with the alarms to be cleared.
*
* @returns A Promise that resolves with an object with a flag `resolvedImmediately`. The flag is true if all alarms for all selected severities were cleared successfully; otherwise false.
*
* **Example**
* ```typescript
* const severitiesToClear: SeverityType[] = [Severity.MAJOR, Severity.MINOR];
*
* clearAllActiveAlarms(severitiesToClear).then(({ resolvedImmediately }) => {
* if (resolvedImmediately) {
* console.log('All selected alarms were cleared successfully.');
* } else {
* console.log('Some alarms could not be cleared.');
* }
* });
* ```
*
* **Note**
* - The method uses the `alarmService.updateBulk` for each severity to clear the active alarms.
* - It may fetch the `sourceId` based on the view (if applicable) and include it as a query parameter in the update calls.
* - The method returns immediately but the returned Promise needs to have a `then` or `catch` method call to handle the result or error respectively.
* - Uses `Promise.all` to wait for all update requests to complete before resolving the final result.
*/
async clearAllActiveAlarms(selectedSeverities, sourceId) {
const severitiesToUpdate = selectedSeverities || Severity;
const promises = Object.values(severitiesToUpdate).map((severity) => {
const commonParams = { resolved: false, severity };
const parameters = sourceId
? {
...commonParams,
source: sourceId,
withSourceAssets: true,
withSourceDevices: true
}
: commonParams;
return this.alarmService.updateBulk({ status: AlarmStatus.CLEARED }, parameters);
});
const responses = await Promise.all(promises);
return {
resolvedImmediately: responses.every(res => res)
};
}
/**
* Returns the correct link based on the provided context data.
* @param contextData The context the navigation was triggered from.
* @param alarm The alarm to navigate to.
* @returns A link to be used as an url navigation.
*/
getRouterLink(contextData, alarm) {
let detailUrl = `/${ALARMS_PATH}`;
if (alarm) {
detailUrl = `/${ALARMS_PATH}/${alarm.id}`;
}
if (!contextData) {
return detailUrl;
}
switch (contextData.context) {
case ViewContext.Device:
return `/device/${contextData.contextData.id}${detailUrl}`;
case ViewContext.Group:
return `/group/${contextData.contextData.id}${detailUrl}`;
case ViewContext.Simulators:
return `/simulators/${contextData.contextData.id}${detailUrl}`;
default:
return detailUrl;
}
}
/**
* Returns the correct array navigation.
* @param contextData The context the navigation was triggered from.
* @param alarm The alarm to navigate to.
* @returns A link to be used as a router.navigation.
*/
getRouterNavigationArray(contextData, alarm) {
return this.getRouterLink(contextData, alarm).split('/').filter(Boolean);
}
/**
* Closes the details view and navigates based on the current route context,
* preserving existing query parameters.
*/
async closeDetailsView(activatedRoute) {
const contextData = this.contextRouteService.getContextData(activatedRoute);
await this.router.navigate(this.getRouterNavigationArray(contextData), {
queryParamsHandling: 'merge'
});
this.updateIntervalState(true);
}
/**
* Returns the correct from and to dates based on the selected interval
* @param intervalId the selected interval. E.g. 'none', 'hours', 'custom' ...
* @returns The calculated date context based on the selected interval.
*/
getDateTimeContextByInterval(intervalId) {
const interval = INTERVALS_EXTENDED.find(({ id }) => id === intervalId);
if (interval.id === 'none') {
return [new Date(0), new Date()];
}
const dateTo = new Date();
const dateFrom = new Date(dateTo.valueOf() - interval.timespanInMs);
return [dateFrom, dateTo];
}
/**
* Converts a given number of seconds into a formatted string representing hours, minutes, and seconds.
*
* @param totalSeconds - The total number of seconds to convert.
* @returns A string in the format "HH:MM:SS", where HH is hours, MM is minutes, and SS is seconds.
*/
convertSecondsToTime(totalSeconds) {
const hours = Math.floor(totalSeconds / 3600);
const minutes = Math.floor((totalSeconds % 3600) / 60);
const seconds = Math.floor(totalSeconds % 60);
const paddedHours = hours.toString().padStart(2, '0');
const paddedMinutes = minutes.toString().padStart(2, '0');
const paddedSeconds = seconds.toString().padStart(2, '0');
return `${paddedHours}:${paddedMinutes}:${paddedSeconds}`;
}
/**
* Creates a value for query parameter for filtering alarms by severity based on array of selected severities.
*
* @param severities - An array of alarm severity types to include in the filter.
* If the array is empty or undefined, no severity filter will be applied.
*
* @returns A comma-separated string of selected alarm severities,
* or null if no severities are provided.
*/
getSeverityQueryParameter(severities) {
if (!severities || severities.length === 0) {
return;
}
if (severities.length === Object.keys(Severity).length) {
return;
}
return severities.join(',');
}
/**
* Creates a value for query parameter for filtering alarms by statuses based on showCleared option.
*
* @param showCleared - A flag indicating whether to include cleared statuses.
* If true, all statuses, including 'CLEARED', will be included; if false, 'CLEARED' will be excluded.
*
* @returns A comma-separated string of alarm statuses.
*/
getStatusQueryParameter(showCleared) {
const statuses = Object.keys(ALARM_STATUS_LABELS);
const filteredStatuses = showCleared
? statuses
: statuses.filter(status => status !== 'CLEARED');
return filteredStatuses.join(',');
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AlarmsViewService, deps: [{ token: i2.AlarmService }, { token: i3.OptionsService }, { token: i1.Router }, { token: i3.ContextRouteService }], target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AlarmsViewService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AlarmsViewService, decorators: [{
type: Injectable,
args: [{
providedIn: 'root'
}]
}], ctorParameters: () => [{ type: i2.AlarmService }, { type: i3.OptionsService }, { type: i1.Router }, { type: i3.ContextRouteService }] });
class AlarmsActivityTrackerService {
constructor() {
this.isUserActive$ = new BehaviorSubject(true);
this.userSecondsSpendOnPage = 0;
this.INACTIVITY_THRESHOLD_SECONDS = 10;
this.ONE_SECOND_IN_MILLISECONDS = 1_000;
this.destroy$ = new Subject();
}
ngOnDestroy() {
this.destroy$.next();
this.destroy$.complete();
}
setGainsightInterval() {
this.gainsightTimerId = setInterval(() => this.userSecondsSpendOnPage++, this.ONE_SECOND_IN_MILLISECONDS);
}
clearGainsightInterval() {
clearInterval(this.gainsightTimerId);
}
resetInactivityTimer() {
this.isUserActive$.next(true);
clearTimeout(this.gainsightInactivityTimeoutId);
this.gainsightInactivityTimeoutId = setTimeout(() => {
this.isUserActive$.next(false); // Pause counting if the user is inactive
}, this.INACTIVITY_THRESHOLD_SECONDS * this.ONE_SECOND_IN_MILLISECONDS);
}
setupEventListenersForGainsight() {
const events = ['mousemove', 'keydown', 'click'];
events.forEach(event => {
fromEvent(window, event)
.pipe(debounceTime(30), takeUntil(this.destroy$))
.subscribe(() => this.resetInactivityTimer());
});
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AlarmsActivityTrackerService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AlarmsActivityTrackerService }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AlarmsActivityTrackerService, decorators: [{
type: Injectable
}] });
/**
* Pipe for transforming alarm severity types into corresponding icons.
*
* @example
* Usage in an Angular template:
* {{ 'CRITICAL' | AlarmSeverityToIcon }}
* Result: 'exclamation-circle'
*/
class AlarmSeverityToIconPipe {
/**
* Transforms an alarm severity type into a corresponding icon.
*
* @param alarmSeverity - The severity type of the alarm.
* @returns The corresponding icon for the given alarm severity type.
*/
transform(alarmSeverity) {
const alarmSeverityMapped = Severity[alarmSeverity?.toUpperCase()];
return ALARM_SEVERITY_ICON_MAP[alarmSeverityMapped] || HELP_ICON;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AlarmSeverityToIconPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "19.2.14", ngImport: i0, type: AlarmSeverityToIconPipe, isStandalone: true, name: "AlarmSeverityToIcon" }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AlarmSeverityToIconPipe, decorators: [{
type: Pipe,
args: [{
name: 'AlarmSeverityToIcon',
standalone: true
}]
}] });
/**
* Angular pipe for transforming alarm statuses into corresponding icons.
*
* @example
* Usage in an Angular template:
* {{ 'ACTIVE' | AlarmStatusToIcon }}
* Result: 'bell'
*/
class AlarmStatusToIconPipe {
/**
* Transforms an alarm status into a corresponding icon.
*
* @param alarmStatus - The status of the alarm.
* @returns - The corresponding icon for the given alarm status.
*/
transform(alarmStatus) {
return AlarmIconMap[alarmStatus?.toUpperCase()] || HELP_ICON;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AlarmStatusToIconPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "19.2.14", ngImport: i0, type: AlarmStatusToIconPipe, isStandalone: true, name: "AlarmStatusToIcon" }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AlarmStatusToIconPipe, decorators: [{
type: Pipe,
args: [{ name: 'AlarmStatusToIcon' }]
}] });
/**
* A pipe for transforming audit record data into localized messages.
* It specifically addresses changes in the audit records, with an emphasis on status changes.
*/
class AuditChangesMessagePipe {
constructor(translateService) {
this.translateService = translateService;
}
/**
* Transforms an IAuditRecord into a localized string message.
* If the record contains changes, and if the first change is related to the 'status' attribute,
* it formats a message indicating the status change. Otherwise, it returns a general activity message.
* Example when there is a status change: "Alarm status changed from ACKNOWLEDGED to ACTIVE".
* Example when record does not have a status attribute: "Alarm updated".
*
* @param record - The audit record to be transformed.
* @returns The localized message describing the audit record,
* particularly focusing on status changes if applicable.
*/
transform(record) {
const firstItem = !!record.changes && Array.from(record.changes)[0];
if (!firstItem || firstItem.attribute !== 'status') {
const activityString = gettext(record.activity);
return this.translateService.instant(activityString);
}
const { newValue, previousValue } = firstItem;
const message = gettext(`Alarm status changed from {{ previousValue }} to {{ newValue }}`);
return this.translateService.instant(message, {
previousValue: this.translateService.instant(previousValue),
newValue: this.translateService.instant(newValue)
});
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AuditChangesMessagePipe, deps: [{ token: i1$1.TranslateService }], target: i0.ɵɵFactoryTarget.Pipe }); }
static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "19.2.14", ngImport: i0, type: AuditChangesMessagePipe, isStandalone: true, name: "auditChangesMessage" }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AuditChangesMessagePipe, decorators: [{
type: Pipe,
args: [{ name: 'auditChangesMessage' }]
}], ctorParameters: () => [{ type: i1$1.TranslateService }] });
class AlarmDetailsComponent {
constructor(alarmDetailsService, alarmService, alertService, appState, auditService, relativeTime, ng1SmartRulesUpgradeService, translateService, inventoryService, alarmsViewService, colorService, interAppService, gainsightService, alarmsActivityTrackerService) {
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.gainsightService = gainsightService;
this.alarmsActivityTrackerService = alarmsActivityTrackerService;
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.PRODUCT_EXPERIENCE_ALARMS = PRODUCT_EXPERIENCE_ALARMS;
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;
this.USER_MINIMUM_SPEND_TIME_SECONDS_TO_TRIGGER_EVENT = 1;
this.destroy$ = new Subject();
}
async ngOnInit() {
this.alarmsActivityTrackerService.setupEventListenersForGainsight();
this.alarmsActivityTrackerService.resetInactivityTimer();
this.alarmsActivityTrackerService.isUserActive$
.pipe(distinctUntilChanged(), takeUntil(this.destroy$))
.subscribe(isActive => isActive
? this.alarmsActivityTrackerService.setGainsightInterval()
: this.alarmsActivityTrackerService.clearGainsightInterval());
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);
}
}
ngOnDestroy() {
if (this.alarmsActivityTrackerService.userSecondsSpendOnPage >=
this.USER_MINIMUM_SPEND_TIME_SECONDS_TO_TRIGGER_EVENT) {
this.gainsightService.triggerEvent(PRODUCT_EXPERIENCE_ALARMS.EVENTS.ALARMS, {
component: PRODUCT_EXPERIENCE_ALARMS.COMPONENTS.ALARM_DETAILS,
action: PRODUCT_EXPERIENCE_ALARMS.ACTIONS.USER_SPEND_TIME_ON_COMPONENT,
userSpendTime: this.alarmsViewService.convertSecondsToTime(this.alarmsActivityTrackerService.userSecondsSpendOnPage)
});
}
this.alarmsActivityTrackerService.clearGainsightInterval();
this.destroy$.next();
this.destroy$.complete();
}
visibilityChange() {
if (document.hidden) {
this.alarmsActivityTrackerService.clearGainsightInterval();
return;
}
this.alarmsActivityTrackerService.setGainsightInterval();
}
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]);
if (status === AlarmStatus.CLEARED) {
this.alarmsViewService.closeDetailsView$.next();
}
}
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: "19.2.14", ngImport: i0, type: AlarmDetailsComponent, deps: [{ token: AlarmDetailsService }, { token: i2.AlarmService }, { token: i3.AlertService }, { token: i3.AppStateService }, { token: i2.AuditService }, { token: i3.RelativeTimePipe }, { token: Ng1SmartRulesUpgradeService, optional: true }, { token: i1$1.TranslateService }, { token: i2.InventoryService }, { token: AlarmsViewService }, { token: i3.ColorService }, { token: i3.InterAppService }, { token: i3.GainsightService }, { token: AlarmsActivityTrackerService }], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.14", type: AlarmDetailsComponent, isStandalone: true, selector: "c8y-alarm-details", inputs: { selectedAlarm: "selectedAlarm" }, host: { listeners: { "document:visibilitychange": "visibilityChange()" } }, providers: [AlarmsActivityTrackerService], 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 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\n class=\"badge badge-info\"\n data-cy=\"c8y-alarm-details--badge\"\n >\n {{ selectedAlarm.count }}\n </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 c8yProductExperience\n [actionName]=\"PRODUCT_EXPERIENCE_ALARMS.EVENTS.ALARMS\"\n [actionData]=\"{\n component: PRODUCT_EXPERIENCE_ALARMS.COMPONENTS.ALARM_DETAILS,\n action: PRODUCT_EXPERIENCE_ALARMS.ACTIONS.RELOAD_AUDIT_LOGS\n }\"\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 c8yProductExperience\n [actionName]=\"PRODUCT_EXPERIENCE_ALARMS.EVENTS.ALARMS\"\n [actionData]=\"{\n component: PRODUCT_EXPERIENCE_ALARMS.COMPONENTS.ALARM_DETAILS,\n action:\n selectedAlarm.status !== ACKNOWLEDGED_STATUS_VALUE\n ? PRODUCT_EXPERIENCE_ALARMS.ACTIONS.ACKNOWLEDGE_ALARM\n : PRODUCT_EXPERIENCE_ALARMS.ACTIONS.REACTIVATE_ALARM\n }\"\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 c8yProductExperience\n [actionName]=\"PRODUCT_EXPERIENCE_ALARMS.EVENTS.ALARMS\"\n [actionData]=\"{\n component: PRODUCT_EXPERIENCE_ALARMS.COMPONENTS.ALARM_DETAILS,\n action: PRODUCT_EXPERIENCE_ALARMS.ACTIONS.CREATE_SMART_RULE\n }\"\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 c8yProductExperience\n [actionName]=\"PRODUCT_EXPERIENCE_ALARMS.EVENTS.ALARMS\"\n [actionData]=\"{\n component: PRODUCT_EXPERIENCE_ALARMS.COMPONENTS.ALARM_DETAILS,\n action: PRODUCT_EXPERIENCE_ALARMS.ACTIONS.CLEAR_ALARM\n }\"\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 | c8yDate: 'mediumDate' }}\n {{ log.creationTime | c8yDate: '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: "directive", type: IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: 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: ProductExperienceDirective, selector: "[c8yProductExperience]", inputs: ["actionName", "actionData", "inherit", "suppressDataOverriding"] }, { kind: "directive", type: NgFor, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "component", type: EmptyStateComponent, selector: "c8y-ui-empty-state", inputs: ["icon", "title", "subtitle", "horizontal"] }, { kind: "component", type: LoadingComponent, selector: "c8y-loading", inputs: ["layout", "progress", "message"] }, { kind: "component", type: ListGroupComponent, selector: "c8y-list-group" }, { kind: "directive", type: 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: ListItemTimelineComponent, selector: "c8y-list-item-timeline, c8y-li-timeline" }, { kind: "component", type: ListItemComponent, selector: "c8y-list-item, c8y-li", inputs: ["active", "highlighted", "emptyActions", "dense", "collapsed", "selectable"], outputs: ["collapsedChange"] }, { kind: "component", type: ListItemBodyComponent, selector: "c8y-list-item-body, c8y-li-body", inputs: ["body"] }, { kind: "pipe", type: C8yTranslatePipe, name: "translate" }, { kind: "pipe", type: AsyncPipe, name: "async" }, { kind: "pipe", type: LowerCasePipe, name: "lowercase" }, { kind: "pipe", type: JsonPipe, name: "json" }, { kind: "pipe", type: DatePipe, name: "c8yDate" }, { kind: "pipe", type: HumanizeAppNamePipe, name: "humanizeAppName" }, { kind: "pipe", type: AssetLinkPipe, name: "assetLink" }, { kind: "pipe", type: AlarmDetailsButtonPipe, name: "alarmDetailsButton" }, { kind: "pipe", type: AlarmSeverityToIconPipe, name: "AlarmSeverityToIcon" }, { kind: "pipe", type: AlarmStatusToIconPipe, name: "AlarmStatusToIcon" }, { kind: "pipe", type: AuditChangesMessagePipe, name: "auditChangesMessage" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AlarmDetailsComponent, decorators: [{
type: Component,
args: [{ selector: 'c8y-alarm-details', providers: [AlarmsActivityTrackerService], imports: [
IconDirective,
NgClass,
RouterLink,
NgIf,
NgStyle,
PopoverDirective,
ProductExperienceDirective,
NgFor,
EmptyStateComponent,
LoadingComponent,
ListGroupComponent,
ForOfDirective,
ListItemTimelineComponent,
ListItemComponent,
ListItemBodyComponent,
C8yTranslatePipe,
AsyncPipe,
LowerCasePipe,
JsonPipe,
DatePipe,
HumanizeAppNamePipe,
AssetLinkPipe,
AlarmDetailsButtonPipe,
AlarmSeverityToIconPipe,
AlarmStatusToIconPipe,
AuditChangesMessagePipe
], 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 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\n class=\"badge badge-info\"\n data-cy=\"c8y-alarm-details--badge\"\n >\n {{ selectedAlarm.count }}\n </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 c8yProductExperience\n [actionName]=\"PRODUCT_EXPERIENCE_ALARMS.EVENTS.ALARMS\"\n [actionData]=\"{\n component: PRODUCT_EXPERIENCE_ALARMS.COMPONENTS.ALARM_DETAILS,\n action: PRODUCT_EXPERIENCE_ALARMS.ACTIONS.RELOAD_AUDIT_LOGS\n }\"\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 c8yProductExperience\n [actionName]=\"PRODUCT_EXPERIENCE_ALARMS.EVENTS.ALARMS\"\n [actionData]=\"{\n component: PRODUCT_EXPERIENCE_ALARMS.COMPONENTS.ALARM_DETAILS,\n action:\n selectedAlarm.status !== ACKNOWLEDGED_STATUS_VALUE\n ? PRODUCT_EXPERIENCE_ALARMS.ACTIONS.ACKNOWLEDGE_ALARM\n : PRODUCT_EXPERIENCE_ALARMS.ACTIONS.REACTIVATE_ALARM\n }\"\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 c8yProductExperience\n [actionName]=\"PRODUCT_EXPERIENCE_ALARMS.EVENTS.ALARMS\"\n [actionData]=\"{\n component: PRODUCT_EXPERIENCE_ALARMS.COMPONENTS.ALARM_DETAILS,\n action: PRODUCT_EXPERIENCE_ALARMS.ACTIONS.CREATE_SMART_RULE\n }\"\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 c8yProductExperience\n [actionName]=\"PRODUCT_EXPERIENCE_ALARMS.EVENTS.ALARMS\"\n [actionData]=\"{\n component: PRODUCT_EXPERIENCE_ALARMS.COMPONENTS.ALARM_DETAILS,\n action: PRODUCT_EXPERIENCE_ALARMS.ACTIONS.CLEAR_ALARM\n }\"\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 | c8yDate: 'mediumDate' }}\n {{ log.creationTime | c8yDate: '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: AlarmDetailsService }, { type: i2.AlarmService }, { type: i3.AlertService }, { type: i3.AppStateService }, { type: i2.AuditService }, { type: i3.RelativeTimePipe }, { type: Ng1SmartRulesUpgradeService, decorators: [{
type: Optional
}] }, { type: i1$1.TranslateService }, { type: i2.InventoryService }, { type: AlarmsViewService }, { type: i3.ColorService }, { type: i3.InterAppService }, { type: i3.GainsightService }, { type: AlarmsActivityTrackerService }], propDecorators: { selectedAlarm: [{
type: Input
}], visibilityChange: [{
type: HostListener,
args: ['document:visibilitychange']
}] } });
class AlarmEmptyComponent {
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AlarmEmptyComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.14", type: AlarmEmptyComponent, isStandalone: true, selector: "c8y-alarms-empty", ngImport: i0, template: "<div class=\"p-24 hidden-sm\" data-cy=\"c8y-alarms-empty\">\n <c8y-ui-empty-state\n [icon]=\"'alarm'\"\n [title]=\"'No alarm selected' | translate\"\n [subtitle]=\"'Select an alarm from the list to view its details.' | translate\"\n [horizontal]=\"true\"\n ></c8y-ui-empty-state>\n</div>\n", dependencies: [{ kind: "component", type: EmptyStateComponent, selector: "c8y-ui-empty-state", inputs: ["icon", "title", "subtitle", "horizontal"] }, { kind: "pipe", type: C8yTranslatePipe, name: "translate" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AlarmEmptyComponent, decorators: [{
type: Component,
args: [{ selector: 'c8y-alarms-empty', imports: [EmptyStateComponent, C8yTranslatePipe], template: "<div class=\"p-24 hidden-sm\" data-cy=\"c8y-alarms-empty\">\n <c8y-ui-empty-state\n [icon]=\"'alarm'\"\n [title]=\"'No alarm selected' | translate\"\n [subtitle]=\"'Select an alarm from the list to view its details.' | translate\"\n [horizontal]=\"true\"\n ></c8y-ui-empty-state>\n</div>\n" }]
}] });
class AlarmInfoComponent {
constructor(activatedRoute, router, contextRouteService, alarmsViewService) {
this.activatedRoute = activatedRoute;
this.router = router;
this.contextRouteService = contextRouteService;
this.alarmsViewService = alarmsViewService;
this.isContextRoute = false;
this.TITLE = gettext('Alarms');
}
async ngOnInit() {
const contextData = this.contextRouteService.getContextData(this.activatedRoute);
this.selectedAlarm$ = of(contextData.contextData);
this.isContextRoute = this.contextRouteService.isContextRoute(this.router.url, [
ViewContext.Device,
ViewContext.Group
]);
}
back() {
this.alarmsViewService.closeDetailsView$.next();
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AlarmInfoComponent, deps: [{ token: i1.ActivatedRoute }, { token: i1.Router }, { token: i3.ContextRouteService }, { token: AlarmsViewService }], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.14", type: AlarmInfoComponent, isStandalone: true, selector: "c8y-alarm-info", ngImport: i0, template: "<ng-container *ngIf=\"!isContextRoute\">\n <c8y-title>{{ TITLE | translate }}</c8y-title>\n</ng-container>\n\n<div\n class=\"inner-scroll split-view__detail split-view__detail--selected\"\n *ngIf=\"selectedAlarm$ | async\"\n>\n <div class=\"sticky-top bg-component\">\n <div class=\"card-header gap-16 d-block-xs d-block-sm p-l-24 p-r-24 p-t-16 separator\">\n <button\n class=\"btn btn-clean text-primary visible-sm visible-xs\"\n data-cy=\"c8y-alarms-info--back\"\n [title]=\"'Back' | translate\"\n (click)=\"back()\"\n >\n <i c8yIcon=\"chevron-left\"></i>\n <span>{{ 'Back' | translate }}</span>\n </button>\n\n <div class=\"flex-no-shrink a-s-start\"></div>\n <div class=\"flex-grow d-col\">\n <div\n class=\"text-break-word flex-grow text-16\"\n data-cy=\"c8y-alarms-info--title\"\n >\n {{ (selectedAlarm$ | async)?.text | translate }}\n </div>\n </div>\n </div>\n <div class=\"p-relative\">\n <c8y-tabs-outlet\n outletName=\"alarms\"\n orientation=\"horizontal\"\n ></c8y-tabs-outlet>\n </div>\n <div class=\"card-block overflow-visible p-l-24 p-r-24\">\n <c8y-alarm-details [selectedAlarm]=\"selectedAlarm$ | async\"></c8y-alarm-details>\n </div>\n </div>\n</div>\n", dependencies: [{ kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: TitleComponent, selector: "c8y-title", inputs: ["pageTitleUpdate"] }, { kind: "directive", type: IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "component", type: TabsOutletComponent, selector: "c8y-tabs-outlet,c8y-ui-tabs", inputs: ["tabs", "orientation", "navigatorOpen", "outletName", "context", "openFirstTab", "hasHeader"] }, { kind: "component", type: AlarmDetailsComponent, selector: "c8y-alarm-details", inputs: ["selectedAlarm"] }, { kind: "pipe", type: C8yTranslatePipe, name: "translate" }, { kind: "pipe", type: AsyncPipe, name: "async" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AlarmInfoComponent, decorators: [{
type: Component,
args: [{ selector: 'c8y-alarm-info', imports: [
NgIf,
TitleComponent,
IconDirective,
TabsOutletComponent,
AlarmDetailsComponent,
C8yTranslatePipe,
AsyncPipe
], template: "<ng-container *ngIf=\"!isContextRoute\">\n <c8y-title>{{ TITLE | translate }}</c8y-title>\n</ng-container>\n\n<div\n class=\"inner-scroll split-view__detail split-view__detail--selected\"\n *ngIf=\"selectedAlarm$ | async\"\n>\n <div class=\"sticky-top bg-component\">\n <div class=\"card-header gap-16 d-block-xs d-block-sm p-l-24 p-r-24 p-t-16 separator\">\n <button\n class=\"btn btn-clean text-primary visible-sm visible-xs\"\n data-cy=\"c8y-alarms-info--back\"\n [title]=\"'Back' | translate\"\n (click)=\"back()\"\n >\n <i c8yIcon=\"chevron-left\"></i>\n <span>{{ 'Back' | translate }}</span>\n </button>\n\n <div class=\"flex-no-shrink a-s-start\"></div>\n <div class=\"flex-grow d-col\">\n <div\n class=\"text-break-word flex-grow text-16\"\n data-cy=\"c8y-alarms-info--title\"\n >\n {{ (selectedAlarm$ | async)?.text | translate }}\n </div>\n </div>\n </div>\n <div class=\"p-relative\">\n <c8y-tabs-outlet\n outletName=\"alarms\"\n orientation=\"horizontal\"\n ></c8y-tabs-outlet>\n </div>\n <div class=\"card-block overflow-visible p-l-24 p-r-24\">\n <c8y-alarm-details [selectedAlarm]=\"selectedAlarm$ | async\"></c8y-alarm-details>\n </div>\n </div>\n</div>\n" }]
}], ctorParameters: () => [{ type: i1.ActivatedRoute }, { type: i1.Router }, { type: i3.ContextRouteService }, { type: AlarmsViewService }] });
/**
* A service to retrieve custom indicators for the alarm list view.
*/
class AlarmListIndicatorService {
constructor(serviceRegistry, pluginsResolver) {
this.serviceRegistry = serviceRegistry;
this.pluginsResolver = pluginsResolver;
}
get$(alarm) {
const providers$ = this.pluginsResolver.allPluginsLoaded$.pipe(filter(Boolean), map(() => {
return this.serviceRegistry.get('alarmListIndicator');
}));
return providers$.pipe(switchMap(providers => {
const observables$ = providers.map(provider => provider.getAlarmListIndicator$(alarm).pipe(startWith(false)));
return combineLatest(observables$);
}), map(indicators => {
return indicators.filter(Boolean);
}), map(indicators => sortBy(indicators, this.byPriority)));
}
byPriority(item) {
if (item.priority === undefined) {
return 0;
}
return -item.priority;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AlarmListIndicatorService, deps: [{ token: i3.ServiceRegistry }, { token: i3.PluginsResolveService }], target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AlarmListIndicatorService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AlarmListIndicatorService, decorators: [{
type: Injectable,
args: [{ providedIn: 'root' }]
}], ctorParameters: () => [{ type: i3.ServiceRegistry }, { type: i3.PluginsResolveService }] });
/**
* A pipe to provide custom indicators for the alarm list view.
*
* Will call `get$()` method of `AlarmListIndicatorService` to get the custom indicators for the provided alarm.
*/
class AlarmListIndicatorPipe {
constructor(alarmListIndicatorService) {
this.alarmListIndicatorService = alarmListIndicatorService;
}
transform(alarm) {
return this.alarmListIndicatorService.get$(alarm);
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AlarmListIndicatorPipe, deps: [{ token: AlarmListIndicatorService }], target: i0.ɵɵFactoryTarget.Pipe }); }
static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "19.2.14", ngImport: i0, type: AlarmListIndicatorPipe, isStandalone: true, name: "alarmListIndicator" }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AlarmListIndicatorPipe, decorators: [{
type: Pipe,
args: [{
standalone: true,
name: 'alarmListIndicator',
pure: true
}]
}], ctorParameters: () => [{ type: AlarmListIndicatorService }] });
/**
* Pipe for transforming an array of alarm severity types into a comma-separated string.
*
* @example
* Usage in a template: {{ ['WARNING', 'CRITICAL'] | AlarmSeveritiesToTitle }}
* Result: 'Warning, Critical'
*/
class AlarmSeveritiesToTitlePipe {
constructor(translateService) {
this.translateService = translateService;
this.severityOptionsCount = Object.keys(SEVERITY_LABELS).length;
}
/**
* Transforms an array of alarm severity types into a comma-separated string.
*
* @param severities - Array of severity types.
* @returns - Transformed human-readable title string.
*/
transform(severities) {
const uniqueSeverities = [...new Set(severities)];
if (uniqueSeverities.some(severity => !(severity in SEVERITY_LABELS))) {
return null;
}
if (uniqueSeverities.length === this.severityOptionsCount) {
return this.translateService.instant(gettext('All alarms'));
}
const translatedChips = uniqueSeverities.map(severity => this.translateSeverityLabel(severity));
return translatedChips.join(', ');
}
/**
* Translates and converts a severity type to title case.
*
* @private
* @param chip - Severity type.
* @returns - Translated and title-cased severity type.
*/
translateSeverityLabel(chip) {
return this.translateService.instant(SEVERITY_LABELS[chip]);
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AlarmSeveritiesToTitlePipe, deps: [{ token: i1$1.TranslateService }], target: i0.ɵɵFactoryTarget.Pipe }); }
static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "19.2.14", ngImport: i0, type: AlarmSeveritiesToTitlePipe, isStandalone: true, name: "AlarmSeveritiesToTitle" }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AlarmSeveritiesToTitlePipe, decorators: [{
type: Pipe,
args: [{ name: 'AlarmSeveritiesToTitle' }]
}], ctorParameters: () => [{ type: i1$1.TranslateService }] });
/**
* Pipe to transform alarm severity to corresponding label.
*/
class AlarmSeverityToLabelPipe {
constructor(translateService) {
this.translateService = translateService;
}
/**
* Transforms an alarm severity to its corresponding label.
* @param alarmSeverity - The alarm severity to transform.
* @returns The translated label corresponding to the given alarm severity.
*/
transform(alarmSeverity) {
const alarmStatusMapped = SEVERITY_LABELS[alarmSeverity?.toUpperCase()];
return this.translateService.instant(alarmStatusMapped ?? alarmSeverity);
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AlarmSeverityToLabelPipe, deps: [{ token: i1$1.TranslateService }], target: i0.ɵɵFactoryTarget.Pipe }); }
static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "19.2.14", ngImport: i0, type: AlarmSeverityToLabelPipe, isStandalone: true, name: "AlarmSeverityToLabel" }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AlarmSeverityToLabelPipe, decorators: [{
type: Pipe,
args: [{ name: 'AlarmSeverityToLabel' }]
}], ctorParameters: () => [{ type: i1$1.TranslateService }] });
/**
* Pipe to transform alarm status to corresponding label.
*/
class AlarmStatusToLabelPipe {
constructor(translateService) {
this.translateService = translateService;
}
/**
* Transforms an alarm status to its corresponding label.
* @param alarmStatus - The alarm status to transform.
* @returns The translated label corresponding to the given alarm status.
*/
transform(alarmStatus) {
return this.translateService.instant(ALARM_STATUS_LABELS[alarmStatus?.toUpperCase()]);
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AlarmStatusToLabelPipe, deps: [{ token: i1$1.TranslateService }], target: i0.ɵɵFactoryTarget.Pipe }); }
static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "19.2.14", ngImport: i0, type: AlarmStatusToLabelPipe, isStandalone: true, name: "AlarmStatusToLabel" }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AlarmStatusToLabelPipe, decorators: [{
type: Pipe,
args: [{ name: 'AlarmStatusToLabel' }]
}], ctorParameters: () => [{ type: i1$1.TranslateService }] });
class AlarmsFilterComponent {
constructor(formBuilder, alarmsViewService, alertService, router, activatedRoute) {
this.formBuilder = formBuilder;
this.alarmsViewService = alarmsViewService;
this.alertService = alertService;
this.router = router;
this.activatedRoute = activatedRoute;
this.severitiesList = Object.keys(SEVERITY_LABELS);
/**
* EventEmitter to notify when filters have been applied.
* Emits a `AlarmListFormFilters` object representing the filter criteria applied by the user.
*/
this.onFilterApplied = new EventEmitter();
this.formGroup = this.formBuilder.group(DEFAULT_SEVERITY_VALUES);
this.chips = [];
this.showCleared = false;
this.alarmCounts = DEFAULT_ALARM_COUNTS;
this.SEVERITY_LABELS = SEVERITY_LABELS;
this.PRODUCT_EXPERIENCE_ALARMS = PRODUCT_EXPERIENCE_ALARMS;
this.isNoneCheckboxSelected$ = new BehaviorSubject(false);
this.severitiesTouched$ = new BehaviorSubject(false);
this.currentFormGroupValues = this.formGroup.value;
this.destroy$ = new Subject();
}
ngOnInit() {
this.activatedRoute.queryParams.pipe(takeUntil(this.destroy$)).subscribe(params => {
this.showCleared = params.showCleared === 'true';
this.formGroup.setValue({
[Severity.CRITICAL]: params[Severity.CRITICAL] === 'true',
[Severity.MAJOR]: params[Severity.MAJOR] === 'true',
[Severity.MINOR]: params[Severity.MINOR] === 'true',
[Severity.WARNING]: params[Severity.WARNING] === 'true'
});
if (params.lastUpdatedFrom) {
this.selectedDates = [new Date(params.lastUpdatedFrom), new Date(params.createdTo)];
}
this.applyFilters(true, false);
});
this.trackCheckboxStateWithFormChanges();
this.currentShowClearedValue = this.showCleared;
this.updateChipsAndDefaultValues();
}
ngAfterViewInit() {
this.filtersDropdown.isOpenChange
.pipe(takeUntil(this.destroy$), filter(Boolean))
.subscribe(() => this.updateAlarmsCount());
}
ngOnDestroy() {
this.destroy$.next();
this.destroy$.complete();
}
allChanged(selected) {
this.formGroup.patchValue({
[Severity.CRITICAL]: selected,
[Severity.MAJOR]: selected,
[Severity.MINOR]: selected,
[Severity.WARNING]: selected
});
}
applyFilters(emit = true, navigate = true) {
this.updateChipsAndDefaultValues();
const combinedFormEvent = {
showCleared: this.showCleared,
severityOptions: this.formGroup.value,
selectedDates: this.selectedDates
};
if (emit) {
this.onFilterApplied.emit(combinedFormEvent);
}
this.currentFormGroupValues = this.formGroup.value;
this.currentShowClearedValue = this.showCleared;
if (navigate) {
this.router.navigate([], {
queryParams: {
showCleared: combinedFormEvent.showCleared,
...combinedFormEvent.severityOptions,
lastUpdatedFrom: combinedFormEvent.selectedDates?.[0]?.toISOString(),
createdTo: combinedFormEvent.selectedDates?.[1]?.toISOString()
},
queryParamsHandling: 'merge'
});
}
}
deselectChip(chip) {
this.formGroup.patchValue({
...this.formGroup.value,
[chip]: false
});
this.applyFilters(false);
this.closeDropdown();
}
closeDropdown() {
if (this.filtersDropdown.isOpen) {
this.filtersDropdown.isOpen = false;
}
}
resetForm() {
this.formGroup.reset(this.currentFormGroupValues);
this.severitiesTouched$.next(false);
this.showCleared = this.currentShowClearedValue;
}
markSeveritiesAsTouched() {
this.severitiesTouched$.next(true);
}
/**
* Asynchronously fetches and updates the count of alarms for each severity level.
*
* @param showCleared - Whether to include cleared alarms in the count.
* Defaults to the current value of `this.currentShowClearedValue`.
* @returns A Promise that resolves when all alarm counts have been fetched,
* or rejects with an error if the operation fails.
* @throws Will throw an error if any of the alarm count fetching promises reject.
*/
async updateAlarmsCount(showCleared = this.currentShowClearedValue) {
this.countLoading = true;
const alarmFilter = this.contextSourceId
? {
source: this.contextSourceId,
withSourceAssets: true,
withSourceDevices: true
}
: null;
const observables = this.severitiesList.reduce((acc, severity) => {
acc[severity] = from(this.alarmsViewService.getAlarmsCountBySeverity(severity, showCleared, alarmFilter));
return acc;
}, {
CRITICAL: null,
MAJOR: null,
MINOR: null,
WARNING: null
});
forkJoin(observables)
.pipe(catchError(err => {
this.alarmCounts = {};
this.alertService.addServerFailure(err);
return EMPTY;
}), finalize(() => (this.countLoading = false)))
.subscribe((alarmCounts) => (this.alarmCounts = alarmCounts));
}
createFormValueWithChangesStream() {
return this.formGroup.valueChanges.pipe(startWith(this.formGroup.value));
}
trackCheckboxStateWithFormChanges() {
const formValue$ = this.createFormValueWithChangesStream();
this.isEachCheckboxSelected$ = this.createAllSelectedStream(formValue$);
this.isIndeterminate$ = this.createIndeterminateStream(formValue$);
this.trackAllCheckboxesDisabled(formValue$);
this.shouldDisableApplyButton$ = this.createDisableApplyButtonStream();
}
createAllSelectedStream(formValue$) {
return formValue$.pipe(map(severities => Object.values(severities).every(Boolean)));
}
createIndeterminateStream(formValue$) {
return formValue$.pipe(map(severities => Object.values(severities).some(Boolean) && !Object.values(severities).every(Boolean)));
}
trackAllCheckboxesDisabled(formValue$) {
formValue$.pipe(takeUntil(this.destroy$)).subscribe(severities => {
const areAllDisabled = Object.values(severities).every(value => !value);
this.isNoneCheckboxSelected$.next(areAllDisabled);
});
}
createDisableApplyButtonStream() {
return combineLatest([this.isNoneCheckboxSelected$, this.severitiesTouched$]).pipe(map(([allCheckboxesAreDisabled, severitiesTouched]) => allCheckboxesAreDisabled || !severitiesTouched));
}
updateChipsAndDefaultValues() {
const severityFilter = this.formGroup;
const seveerityValues = severityFilter.value;
this.chips = Object.keys(seveerityValues).filter(key => seveerityValues[key]);
const allChipsRemoved = this.chips.length === 0;
if (allChipsRemoved) {
const defaultValues = DEFAULT_SEVERITY_VALUES;
severityFilter.setValue(defaultValues);
this.chips = Object.keys(defaultValues);
}
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AlarmsFilterComponent, deps: [{ token: i1$2.FormBuilder }, { token: AlarmsViewService }, { token: i3.AlertService }, { token: i1.Router }, { token: i1.ActivatedRoute }], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.14", type: AlarmsFilterComponent, isStandalone: true, selector: "c8y-alarms-filter", inputs: { contextSourceId: "contextSourceId" }, outputs: { onFilterApplied: "onFilterApplied" }, viewQueries: [{ propertyName: "filtersDropdown", first: true, predicate: ["filtersDropdown"], descendants: true }], ngImport: i0, template: "<form\n class=\"d-flex a-i-center\"\n [formGroup]=\"formGroup\"\n>\n <div\n class=\"dropdown\"\n title=\"{{ 'Filter by severity' | translate }}\"\n dropdown\n #filtersDropdown=\"bs-dropdown\"\n [cdkTrapFocus]=\"filtersDropdown.isOpen\"\n [insideClick]=\"true\"\n >\n <div class=\"input-group fit-w\">\n <div class=\"form-control d-flex a-i-center inner-scroll\" style=\"min-width: 104px; padding-top: 0; padding-bottom: 0;\">\n <ng-container *ngIf=\"chips.length !== severitiesList.length; else allAlarms\">\n <span\n class=\"tag tag--info chip\"\n [attr.data-cy]=\"'c8y-alarms-filter--chip-' + chip\"\n *ngFor=\"let chip of chips\"\n >\n <button\n class=\"btn btn-xs btn-clean text-10\"\n title=\"{{ 'Remove' | translate }}\"\n type=\"button\"\n data-cy=\"c8y-alarms-filter--remove-chip\"\n c8yProductExperience\n [actionName]=\"PRODUCT_EXPERIENCE_ALARMS.EVENTS.ALARMS\"\n [actionData]=\"{\n component: PRODUCT_EXPERIENCE_ALARMS.COMPONENTS.ALARMS_FILTER,\n action: PRODUCT_EXPERIENCE_ALARMS.ACTIONS.REMOVE_CHIP_FILTER,\n filterValues: {\n severities: formGroup.value,\n showCleared: showCleared\n }\n }\"\n (click)=\"deselectChip(chip); $event.stopPropagation()\"\n >\n <i c8yIcon=\"times\"></i>\n </button>\n <i\n class=\"status stroked-icon icon-12\"\n [attr.data-cy]=\"'c8y-alarms-filter--icon-' + chip\"\n [c8yIcon]=\"chip | AlarmSeverityToIcon\"\n [ngClass]=\"chip | lowercase\"\n ></i>\n {{ SEVERITY_LABELS[chip] | translate }}\n </span>\n </ng-container>\n <ng-template #allAlarms>\n <span class=\"text-truncate\" title=\"{{ 'All severities' | translate }}\">\n {{ 'All severities' | translate }}\n </span>\n </ng-template>\n </div>\n <div class=\"input-group-btn input-group-btn--last\">\n <button\n class=\"btn-default btn btn--caret\"\n title=\"{{ chips | AlarmSeveritiesToTitle }}\"\n data-cy=\"c8y-alarms-filter\"\n dropdownToggle\n (click)=\"resetForm()\"\n >\n <i class=\"caret\"></i>\n </button>\n </div>\n </div>\n <ul\n class=\"dropdown-menu dropdown-menu-action-bar\"\n *dropdownMenu\n >\n <li class=\"p-l-16 p-r-16 p-t-4 p-b-4 d-flex a-i-center sticky-top separator-bottom\">\n <label\n class=\"c8y-checkbox d-flex a-i-center\"\n [title]=\"'All' | translate\"\n >\n <input\n type=\"checkbox\"\n data-cy=\"c8y-alarms-filter--all\"\n [ngModelOptions]=\"{ standalone: true }\"\n (ngModelChange)=\"allChanged($event)\"\n [ngModel]=\"isEachCheckboxSelected$ | async\"\n [indeterminate]=\"isIndeterminate$ | async\"\n (click)=\"markSeveritiesAsTouched()\"\n />\n <span></span>\n <i\n class=\"status stroked-icon m-l-8 icon-20\"\n [c8yIcon]=\"'bell'\"\n ></i>\n <span class=\"m-l-8\">{{ 'All' | translate }}</span>\n </label>\n </li>\n <li\n class=\"p-l-16 p-r-16 p-t-4 p-b-4 d-flex a-i-center\"\n *ngFor=\"let severity of severitiesList\"\n >\n <label\n class=\"c8y-checkbox d-flex a-i-center\"\n [title]=\"SEVERITY_LABELS[severity] | translate\"\n >\n <input\n type=\"checkbox\"\n [attr.data-cy]=\"'c8y-alarms-filter--' + severity\"\n [formControlName]=\"severity\"\n [value]=\"severity\"\n (click)=\"markSeveritiesAsTouched()\"\n />\n <span></span>\n <i\n class=\"status stroked-icon m-l-8 icon-20\"\n [c8yIcon]=\"severity | AlarmSeverityToIcon\"\n [ngClass]=\"severity | lowercase\"\n ></i>\n <span class=\"m-l-8\">{{ SEVERITY_LABELS[severity] | translate }}</span>\n </label>\n <!-- badge -->\n <div\n class=\"badge badge-info m-l-auto\"\n *ngIf=\"alarmCounts[severity] || alarmCounts[severity] === 0\"\n >\n <i\n class=\"icon-spin\"\n [c8yIcon]=\"'circle-o-notch'\"\n *ngIf=\"countLoading\"\n ></i>\n <span\n *ngIf=\"!countLoading\"\n [attr.data-cy]=\"'c8y-alarms-filter--' + severity + '-badge'\"\n >\n {{ alarmCounts[severity] < 99 ? alarmCounts[severity] : '99+' }}\n </span>\n </div>\n </li>\n <li class=\"p-l-16 p-r-16 p-t-4 p-b-4 d-flex a-i-center separator-top\">\n <label\n class=\"c8y-switch\"\n [attr.aria-label]=\"'Show cleared alarms' | translate\"\n [attr.data-cy]=\"'c8y-alarms-filter--cleared'\"\n >\n <input\n type=\"checkbox\"\n #showClearedCheckbox\n [ngModelOptions]=\"{ standalone: true }\"\n [(ngModel)]=\"showCleared\"\n (click)=\"markSeveritiesAsTouched(); updateAlarmsCount(showClearedCheckbox.checked)\"\n />\n <span></span>\n <span\n class=\"text-truncate\"\n title=\"{{ 'Show cleared alarms' | translate }}\"\n >\n {{ 'Show cleared alarms' | translate }}\n </span>\n </label>\n </li>\n\n <div class=\"p-16 d-flex sticky-bottom separator-top\">\n <button\n data-cy=\"c8y-alarms-filter--apply\"\n class=\"btn btn-primary btn-sm flex-grow\"\n c8yProductExperience\n [actionName]=\"PRODUCT_EXPERIENCE_ALARMS.EVENTS.ALARMS\"\n [actionData]=\"{\n component: PRODUCT_EXPERIENCE_ALARMS.COMPONENTS.ALARMS_FILTER,\n action: PRODUCT_EXPERIENCE_ALARMS.ACTIONS.APPLY_FILTER,\n filterValues: {\n severities: formGroup.value,\n showCleared: showCleared\n }\n }\"\n title=\"{{ 'Apply' | translate }}\"\n type=\"button\"\n (click)=\"applyFilters(false); closeDropdown()\"\n [disabled]=\"shouldDisableApplyButton$ | async\"\n >\n {{ 'Apply' | translate }}\n </button>\n </div>\n </ul>\n </div>\n</form>\n", dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$2.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$2.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "directive", type: BsDropdownDirective, selector: "[bsDropdown], [dropdown]", inputs: ["placement", "triggers", "container", "dropup", "autoClose", "isAnimated", "insideClick", "isDisabled", "isOpen"], outputs: ["isOpenChange", "onShown", "onHidden"], exportAs: ["bs-dropdown"] }, { kind: "directive", type: CdkTrapFocus, selector: "[cdkTrapFocus]", inputs: ["cdkTrapFocus", "cdkTrapFocusAutoCapture"], exportAs: ["cdkTrapFocus"] }, { kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: NgFor, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: ProductExperienceDirective, selector: "[c8yProductExperience]", inputs: ["actionName", "actionData", "inherit", "suppressDataOverriding"] }, { kind: "directive", type: IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: BsDropdownToggleDirective, selector: "[bsDropdownToggle],[dropdownToggle]", exportAs: ["bs-dropdown-toggle"] }, { kind: "directive", type: BsDropdownMenuDirective, selector: "[bsDropdownMenu],[dropdownMenu]", exportAs: ["bs-dropdown-menu"] }, { kind: "directive", type: RequiredInputPlaceholderDirective, selector: "input[required], input[formControlName]" }, { kind: "pipe", type: C8yTranslatePipe, name: "translate" }, { kind: "pipe", type: AsyncPipe, name: "async" }, { kind: "pipe", type: LowerCasePipe, name: "lowercase" }, { kind: "pipe", type: AlarmSeverityToIconPipe, name: "AlarmSeverityToIcon" }, { kind: "pipe", type: AlarmSeveritiesToTitlePipe, name: "AlarmSeveritiesToTitle" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AlarmsFilterComponent, decorators: [{
type: Component,
args: [{ selector: 'c8y-alarms-filter', imports: [
FormsModule,
ReactiveFormsModule,
BsDropdownDirective,
CdkTrapFocus,
NgIf,
NgFor,
ProductExperienceDirective,
IconDirective,
NgClass,
BsDropdownToggleDirective,
BsDropdownMenuDirective,
RequiredInputPlaceholderDirective,
C8yTranslatePipe,
AsyncPipe,
LowerCasePipe,
AlarmSeverityToIconPipe,
AlarmSeveritiesToTitlePipe
], template: "<form\n class=\"d-flex a-i-center\"\n [formGroup]=\"formGroup\"\n>\n <div\n class=\"dropdown\"\n title=\"{{ 'Filter by severity' | translate }}\"\n dropdown\n #filtersDropdown=\"bs-dropdown\"\n [cdkTrapFocus]=\"filtersDropdown.isOpen\"\n [insideClick]=\"true\"\n >\n <div class=\"input-group fit-w\">\n <div class=\"form-control d-flex a-i-center inner-scroll\" style=\"min-width: 104px; padding-top: 0; padding-bottom: 0;\">\n <ng-container *ngIf=\"chips.length !== severitiesList.length; else allAlarms\">\n <span\n class=\"tag tag--info chip\"\n [attr.data-cy]=\"'c8y-alarms-filter--chip-' + chip\"\n *ngFor=\"let chip of chips\"\n >\n <button\n class=\"btn btn-xs btn-clean text-10\"\n title=\"{{ 'Remove' | translate }}\"\n type=\"button\"\n data-cy=\"c8y-alarms-filter--remove-chip\"\n c8yProductExperience\n [actionName]=\"PRODUCT_EXPERIENCE_ALARMS.EVENTS.ALARMS\"\n [actionData]=\"{\n component: PRODUCT_EXPERIENCE_ALARMS.COMPONENTS.ALARMS_FILTER,\n action: PRODUCT_EXPERIENCE_ALARMS.ACTIONS.REMOVE_CHIP_FILTER,\n filterValues: {\n severities: formGroup.value,\n showCleared: showCleared\n }\n }\"\n (click)=\"deselectChip(chip); $event.stopPropagation()\"\n >\n <i c8yIcon=\"times\"></i>\n </button>\n <i\n class=\"status stroked-icon icon-12\"\n [attr.data-cy]=\"'c8y-alarms-filter--icon-' + chip\"\n [c8yIcon]=\"chip | AlarmSeverityToIcon\"\n [ngClass]=\"chip | lowercase\"\n ></i>\n {{ SEVERITY_LABELS[chip] | translate }}\n </span>\n </ng-container>\n <ng-template #allAlarms>\n <span class=\"text-truncate\" title=\"{{ 'All severities' | translate }}\">\n {{ 'All severities' | translate }}\n </span>\n </ng-template>\n </div>\n <div class=\"input-group-btn input-group-btn--last\">\n <button\n class=\"btn-default btn btn--caret\"\n title=\"{{ chips | AlarmSeveritiesToTitle }}\"\n data-cy=\"c8y-alarms-filter\"\n dropdownToggle\n (click)=\"resetForm()\"\n >\n <i class=\"caret\"></i>\n </button>\n </div>\n </div>\n <ul\n class=\"dropdown-menu dropdown-menu-action-bar\"\n *dropdownMenu\n >\n <li class=\"p-l-16 p-r-16 p-t-4 p-b-4 d-flex a-i-center sticky-top separator-bottom\">\n <label\n class=\"c8y-checkbox d-flex a-i-center\"\n [title]=\"'All' | translate\"\n >\n <input\n type=\"checkbox\"\n data-cy=\"c8y-alarms-filter--all\"\n [ngModelOptions]=\"{ standalone: true }\"\n (ngModelChange)=\"allChanged($event)\"\n [ngModel]=\"isEachCheckboxSelected$ | async\"\n [indeterminate]=\"isIndeterminate$ | async\"\n (click)=\"markSeveritiesAsTouched()\"\n />\n <span></span>\n <i\n class=\"status stroked-icon m-l-8 icon-20\"\n [c8yIcon]=\"'bell'\"\n ></i>\n <span class=\"m-l-8\">{{ 'All' | translate }}</span>\n </label>\n </li>\n <li\n class=\"p-l-16 p-r-16 p-t-4 p-b-4 d-flex a-i-center\"\n *ngFor=\"let severity of severitiesList\"\n >\n <label\n class=\"c8y-checkbox d-flex a-i-center\"\n [title]=\"SEVERITY_LABELS[severity] | translate\"\n >\n <input\n type=\"checkbox\"\n [attr.data-cy]=\"'c8y-alarms-filter--' + severity\"\n [formControlName]=\"severity\"\n [value]=\"severity\"\n (click)=\"markSeveritiesAsTouched()\"\n />\n <span></span>\n <i\n class=\"status stroked-icon m-l-8 icon-20\"\n [c8yIcon]=\"severity | AlarmSeverityToIcon\"\n [ngClass]=\"severity | lowercase\"\n ></i>\n <span class=\"m-l-8\">{{ SEVERITY_LABELS[severity] | translate }}</span>\n </label>\n <!-- badge -->\n <div\n class=\"badge badge-info m-l-auto\"\n *ngIf=\"alarmCounts[severity] || alarmCounts[severity] === 0\"\n >\n <i\n class=\"icon-spin\"\n [c8yIcon]=\"'circle-o-notch'\"\n *ngIf=\"countLoading\"\n ></i>\n <span\n *ngIf=\"!countLoading\"\n [attr.data-cy]=\"'c8y-alarms-filter--' + severity + '-badge'\"\n >\n {{ alarmCounts[severity] < 99 ? alarmCounts[severity] : '99+' }}\n </span>\n </div>\n </li>\n <li class=\"p-l-16 p-r-16 p-t-4 p-b-4 d-flex a-i-center separator-top\">\n <label\n class=\"c8y-switch\"\n [attr.aria-label]=\"'Show cleared alarms' | translate\"\n [attr.data-cy]=\"'c8y-alarms-filter--cleared'\"\n >\n <input\n type=\"checkbox\"\n #showClearedCheckbox\n [ngModelOptions]=\"{ standalone: true }\"\n [(ngModel)]=\"showCleared\"\n (click)=\"markSeveritiesAsTouched(); updateAlarmsCount(showClearedCheckbox.checked)\"\n />\n <span></span>\n <span\n class=\"text-truncate\"\n title=\"{{ 'Show cleared alarms' | translate }}\"\n >\n {{ 'Show cleared alarms' | translate }}\n </span>\n </label>\n </li>\n\n <div class=\"p-16 d-flex sticky-bottom separator-top\">\n <button\n data-cy=\"c8y-alarms-filter--apply\"\n class=\"btn btn-primary btn-sm flex-grow\"\n c8yProductExperience\n [actionName]=\"PRODUCT_EXPERIENCE_ALARMS.EVENTS.ALARMS\"\n [actionData]=\"{\n component: PRODUCT_EXPERIENCE_ALARMS.COMPONENTS.ALARMS_FILTER,\n action: PRODUCT_EXPERIENCE_ALARMS.ACTIONS.APPLY_FILTER,\n filterValues: {\n severities: formGroup.value,\n showCleared: showCleared\n }\n }\"\n title=\"{{ 'Apply' | translate }}\"\n type=\"button\"\n (click)=\"applyFilters(false); closeDropdown()\"\n [disabled]=\"shouldDisableApplyButton$ | async\"\n >\n {{ 'Apply' | translate }}\n </button>\n </div>\n </ul>\n </div>\n</form>\n" }]
}], ctorParameters: () => [{ type: i1$2.FormBuilder }, { type: AlarmsViewService }, { type: i3.AlertService }, { type: i1.Router }, { type: i1.ActivatedRoute }], propDecorators: { contextSourceId: [{
type: Input
}], onFilterApplied: [{
type: Output
}], filtersDropdown: [{
type: ViewChild,
args: ['filtersDropdown']
}] } });
class AlarmsIconComponent {
constructor() {
this.iconBackgroundColor = 'none';
this.isFilterApplied = false;
this.alarmSeverityLabel = gettext('Severity: {{ alarmSeverity }}');
this.alarmStatusLabel = gettext('Status: {{ alarmStatus }}');
this.alarmStatusType = gettext('Type: {{ alarmType }}');
}
ngOnInit() {
this.isFilterApplied = this.typeFilters.length > 0;
this.iconBackgroundColor =
this.typeFilters.find(filter => filter.filters.type === this.alarm.type)?.color || 'none';
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AlarmsIconComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.14", type: AlarmsIconComponent, isStandalone: true, selector: "c8y-alarms-icon", inputs: { alarm: "alarm", typeFilters: "typeFilters" }, ngImport: i0, template: "<button\n class=\"btn-clean severity\"\n [attr.aria-label]=\"\n alarmSeverityLabel | translate: { alarmSeverity: alarm.severity | AlarmSeverityToLabel }\n \"\n [tooltip]=\"\n alarmSeverityLabel | translate: { alarmSeverity: alarm.severity | AlarmSeverityToLabel }\n \"\n placement=\"right\"\n container=\"body\"\n type=\"button\"\n (click)=\"$event.stopPropagation()\"\n [delay]=\"500\"\n>\n <i\n class=\"status stroked-icon\"\n [c8yIcon]=\"alarm.severity | AlarmSeverityToIcon\"\n [ngClass]=\"alarm.severity | lowercase\"\n ></i>\n</button>\n<button\n class=\"btn-clean status\"\n [ngStyle]=\"{ 'background-color': iconBackgroundColor }\"\n [attr.aria-label]=\"\n alarmStatusLabel | translate: { alarmStatus: alarm.status | AlarmStatusToLabel }\n \"\n [tooltip]=\"\n (alarmStatusLabel | translate: { alarmStatus: alarm.status | AlarmStatusToLabel }) +\n '\\n' +\n (alarmStatusType | translate: { alarmType: alarm.type })\n \"\n placement=\"right\"\n container=\"body\"\n type=\"button\"\n [ngClass]=\"{ 'circle-icon-wrapper': isFilterApplied }\"\n data-cy=\"c8y-alarms-icon--status-icon\"\n (click)=\"$event.stopPropagation()\"\n [delay]=\"500\"\n>\n <i\n [c8yIcon]=\"alarm.status | AlarmStatusToIcon\"\n [ngClass]=\"{ 'stroked-icon': isFilterApplied }\"\n ></i>\n</button>\n", dependencies: [{ kind: "directive", type: TooltipDirective, selector: "[tooltip], [tooltipHtml]", inputs: ["adaptivePosition", "tooltip", "placement", "triggers", "container", "containerClass", "boundariesElement", "isOpen", "isDisabled", "delay", "tooltipHtml", "tooltipPlacement", "tooltipIsOpen", "tooltipEnable", "tooltipAppendToBody", "tooltipAnimation", "tooltipClass", "tooltipContext", "tooltipPopupDelay", "tooltipFadeDuration", "tooltipTrigger"], outputs: ["tooltipChange", "onShown", "onHidden", "tooltipStateChanged"], exportAs: ["bs-tooltip"] }, { kind: "directive", type: IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "pipe", type: C8yTranslatePipe, name: "translate" }, { kind: "pipe", type: LowerCasePipe, name: "lowercase" }, { kind: "pipe", type: AlarmSeverityToIconPipe, name: "AlarmSeverityToIcon" }, { kind: "pipe", type: AlarmStatusToIconPipe, name: "AlarmStatusToIcon" }, { kind: "pipe", type: AlarmSeverityToLabelPipe, name: "AlarmSeverityToLabel" }, { kind: "pipe", type: AlarmStatusToLabelPipe, name: "AlarmStatusToLabel" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AlarmsIconComponent, decorators: [{
type: Component,
args: [{ selector: 'c8y-alarms-icon', imports: [
TooltipDirective,
IconDirective,
NgClass,
NgStyle,
C8yTranslatePipe,
LowerCasePipe,
AlarmSeverityToIconPipe,
AlarmStatusToIconPipe,
AlarmSeverityToLabelPipe,
AlarmStatusToLabelPipe
], template: "<button\n class=\"btn-clean severity\"\n [attr.aria-label]=\"\n alarmSeverityLabel | translate: { alarmSeverity: alarm.severity | AlarmSeverityToLabel }\n \"\n [tooltip]=\"\n alarmSeverityLabel | translate: { alarmSeverity: alarm.severity | AlarmSeverityToLabel }\n \"\n placement=\"right\"\n container=\"body\"\n type=\"button\"\n (click)=\"$event.stopPropagation()\"\n [delay]=\"500\"\n>\n <i\n class=\"status stroked-icon\"\n [c8yIcon]=\"alarm.severity | AlarmSeverityToIcon\"\n [ngClass]=\"alarm.severity | lowercase\"\n ></i>\n</button>\n<button\n class=\"btn-clean status\"\n [ngStyle]=\"{ 'background-color': iconBackgroundColor }\"\n [attr.aria-label]=\"\n alarmStatusLabel | translate: { alarmStatus: alarm.status | AlarmStatusToLabel }\n \"\n [tooltip]=\"\n (alarmStatusLabel | translate: { alarmStatus: alarm.status | AlarmStatusToLabel }) +\n '\\n' +\n (alarmStatusType | translate: { alarmType: alarm.type })\n \"\n placement=\"right\"\n container=\"body\"\n type=\"button\"\n [ngClass]=\"{ 'circle-icon-wrapper': isFilterApplied }\"\n data-cy=\"c8y-alarms-icon--status-icon\"\n (click)=\"$event.stopPropagation()\"\n [delay]=\"500\"\n>\n <i\n [c8yIcon]=\"alarm.status | AlarmStatusToIcon\"\n [ngClass]=\"{ 'stroked-icon': isFilterApplied }\"\n ></i>\n</button>\n" }]
}], propDecorators: { alarm: [{
type: Input
}], typeFilters: [{
type: Input
}] } });
class AlarmsIntervalRefreshComponent {
/**
* * Set the value of `isIntervalEnabled` in response to user interactions with the alarm list scroll.
* *
* * This input setter allows you to control the `isIntervalEnabled` property, which is used to manage the state
* * of a toggle button. When a user scrolls through the alarms list, you can update the `isIntervalEnabled` value
* * using this setter.
* *
* * @param value - A boolean value representing the new state of the `isIntervalEnabled` property.
* * - `true` indicates that the interval is enabled.
* * - `false` indicates that the interval is disabled.
*/
set isIntervalToggleEnabled(value) {
const shouldSetInterval = this.isIntervalToggleEnabled || this.doesUserCheckedIntervalToggle;
const shouldToggleInterval = !this.isDisabled &&
this.isIntervalToggleEnabled &&
this.doesUserCheckedIntervalToggle &&
value;
const intervalToggleControl = this.toggleIntervalForm.get('intervalToggle');
/**
* We check if any interactions to toggle interval button were made.
* When user interacts with toggle button, we need to ignore assigning value to the form.
*/
if (intervalToggleControl.dirty && !shouldSetInterval) {
return;
}
/**
* This condition checks if the interval toggle is enabled and if there has been user interaction,
* and if the provided value is truthy.
* If all conditions are met, the interval toggle should not be updated due to unnecessary update of countdown interval component
*/
if (shouldToggleInterval) {
return;
}
intervalToggleControl.setValue(value);
}
/**
* This getter allows you to access the current state of the `isIntervalEnabled` property, which reflects
* the state of a toggle button. It retrieves the value from the associated form control, providing the
* current state of the toggle button.
*/
get isIntervalToggleEnabled() {
return !this.isDisabled && this.toggleIntervalForm.get('intervalToggle').value;
}
constructor(fb, alarmsViewService) {
this.fb = fb;
this.alarmsViewService = alarmsViewService;
this.refreshIntervalsInMilliseconds = this.alarmsViewService.DEFAULT_INTERVAL_VALUES;
this.DISABLE_AUTO_REFRESH = gettext('Disable auto refresh');
this.ENABLE_AUTO_REFRESH = gettext('Enable auto refresh');
this.SECONDS_UNTIL_REFRESH = gettext('{{ seconds }} s');
this.isDisabled = false;
/**
* Event emitter for notifying when a countdown timer has completed.
*/
this.onCountdownEnded = new EventEmitter();
this.toggleIntervalForm = this.initForm();
this.destroy$ = new Subject();
}
ngOnInit() {
this.listenToRefreshIntervalChange();
}
ngAfterViewInit() {
this.onIntervalToggleChange();
this.listenOnLoadingChanges();
}
ngOnDestroy() {
this.destroy$.next();
this.destroy$.complete();
}
resetCountdown() {
this.countdownIntervalComponent?.reset();
}
trackUserClickOnIntervalToggle(target) {
this.doesUserCheckedIntervalToggle = target.checked;
}
getTooltip() {
return this.isDisabled
? gettext('Disabled')
: this.isIntervalToggleEnabled
? this.DISABLE_AUTO_REFRESH
: this.ENABLE_AUTO_REFRESH;
}
startCountdown() {
this.countdownIntervalComponent.start();
}
onIntervalToggleChange() {
this.toggleIntervalForm
.get('intervalToggle')
.valueChanges.pipe(takeUntil(this.destroy$), filter(Boolean))
.subscribe(() => setTimeout(() => this.startCountdown()));
}
initForm() {
return this.fb.group({
intervalToggle: true,
refreshInterval: this.alarmsViewService.DEFAULT_INTERVAL_VALUE
});
}
listenToRefreshIntervalChange() {
this.toggleIntervalForm
.get('refreshInterval')
.valueChanges.pipe(takeUntil(this.destroy$))
.subscribe(() => this.resetCountdown());
}
listenOnLoadingChanges() {
this.alarmsListLoading$
.pipe(tap(() => this.countdownIntervalComponent?.stop()))
.subscribe(state => {
!state && this.countdownIntervalComponent?.reset();
});
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AlarmsIntervalRefreshComponent, deps: [{ token: i1$2.FormBuilder }, { token: AlarmsViewService }], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.14", type: AlarmsIntervalRefreshComponent, isStandalone: true, selector: "c8y-alarms-interval-refresh", inputs: { isDisabled: "isDisabled", alarmsListLoading$: "alarmsListLoading$", isIntervalToggleEnabled: "isIntervalToggleEnabled" }, outputs: { onCountdownEnded: "onCountdownEnded" }, viewQueries: [{ propertyName: "countdownIntervalComponent", first: true, predicate: CountdownIntervalComponent, descendants: true }], ngImport: i0, template: "<form\n class=\"d-flex a-i-center fit-w fit-h\"\n [formGroup]=\"toggleIntervalForm\"\n>\n <label class=\"m-b-0 m-r-8 text-normal text-muted flex-no-shrink\">\n {{ 'Auto refresh' | translate }}\n </label>\n <div class=\"input-group\">\n <label\n class=\"toggle-countdown\"\n [class.toggle-countdown-disabled]=\"isDisabled\"\n [attr.aria-label]=\"getTooltip() | translate\"\n [tooltip]=\"getTooltip() | translate\"\n placement=\"bottom\"\n [adaptivePosition]=\"false\"\n [delay]=\"500\"\n data-cy=\"c8y-alarms-interval-refresh--toggle-countdown\"\n >\n <input\n type=\"checkbox\"\n data-cy=\"c8y-alarms-interval-toggle\"\n formControlName=\"intervalToggle\"\n (click)=\"trackUserClickOnIntervalToggle($event.target)\"\n />\n <c8y-countdown-interval\n *ngIf=\"isIntervalToggleEnabled\"\n [countdownInterval]=\"toggleIntervalForm.value.refreshInterval\"\n (countdownEnded)=\"onCountdownEnded.emit()\"\n ></c8y-countdown-interval>\n <i\n data-cy=\"c8y-alarms-interval-refresh--pause\"\n c8yIcon=\"pause\"\n *ngIf=\"!isIntervalToggleEnabled\"\n ></i>\n </label>\n <div\n class=\"c8y-select-wrapper\"\n *ngIf=\"!isDisabled\"\n >\n <select\n class=\"form-control text-12\"\n [attr.aria-label]=\"'Refresh interval in seconds' | translate\"\n [tooltip]=\"'Refresh interval in seconds' | translate\"\n placement=\"bottom\"\n [adaptivePosition]=\"false\"\n [delay]=\"500\"\n [container]=\"'body'\"\n formControlName=\"refreshInterval\"\n data-cy=\"c8y-alarms-interval-refresh--selector\"\n >\n <option\n [disabled]=\"isDisabled\"\n *ngFor=\"let refreshInterval of refreshIntervalsInMilliseconds\"\n [ngValue]=\"refreshInterval\"\n [attr.data-cy]=\"'c8y-interval-' + refreshInterval\"\n >\n {{ SECONDS_UNTIL_REFRESH | translate: { seconds: refreshInterval / 1000 } }}\n </option>\n </select>\n <span></span>\n </div>\n <div class=\"input-group-btn\">\n <button\n class=\"btn btn-default\"\n style=\"border-left: 0\"\n [attr.aria-label]=\"'Refresh' | translate\"\n [tooltip]=\"'Refresh' | translate\"\n placement=\"bottom\"\n type=\"button\"\n [adaptivePosition]=\"false\"\n [delay]=\"500\"\n [disabled]=\"isDisabled || (alarmsListLoading$ | async)\"\n (click)=\"onCountdownEnded.emit()\"\n data-cy=\"c8y-alarms-interval-refresh--btn\"\n >\n <i\n c8yIcon=\"refresh\"\n [ngClass]=\"{ 'icon-spin': alarmsListLoading$ | async }\"\n ></i>\n </button>\n </div>\n </div>\n</form>\n", dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$2.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$2.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1$2.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1$2.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i1$2.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "directive", type: TooltipDirective, selector: "[tooltip], [tooltipHtml]", inputs: ["adaptivePosition", "tooltip", "placement", "triggers", "container", "containerClass", "boundariesElement", "isOpen", "isDisabled", "delay", "tooltipHtml", "tooltipPlacement", "tooltipIsOpen", "tooltipEnable", "tooltipAppendToBody", "tooltipAnimation", "tooltipClass", "tooltipContext", "tooltipPopupDelay", "tooltipFadeDuration", "tooltipTrigger"], outputs: ["tooltipChange", "onShown", "onHidden", "tooltipStateChanged"], exportAs: ["bs-tooltip"] }, { kind: "directive", type: RequiredInputPlaceholderDirective, selector: "input[required], input[formControlName]" }, { kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: CountdownIntervalComponent, selector: "c8y-countdown-interval", inputs: ["countdownInterval"], outputs: ["countdownEnded"] }, { kind: "directive", type: IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "directive", type: NgFor, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "pipe", type: C8yTranslatePipe, name: "translate" }, { kind: "pipe", type: AsyncPipe, name: "async" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AlarmsIntervalRefreshComponent, decorators: [{
type: Component,
args: [{ selector: 'c8y-alarms-interval-refresh', imports: [
FormsModule,
ReactiveFormsModule,
TooltipDirective,
RequiredInputPlaceholderDirective,
NgIf,
CountdownIntervalComponent,
IconDirective,
NgFor,
NgClass,
C8yTranslatePipe,
AsyncPipe
], template: "<form\n class=\"d-flex a-i-center fit-w fit-h\"\n [formGroup]=\"toggleIntervalForm\"\n>\n <label class=\"m-b-0 m-r-8 text-normal text-muted flex-no-shrink\">\n {{ 'Auto refresh' | translate }}\n </label>\n <div class=\"input-group\">\n <label\n class=\"toggle-countdown\"\n [class.toggle-countdown-disabled]=\"isDisabled\"\n [attr.aria-label]=\"getTooltip() | translate\"\n [tooltip]=\"getTooltip() | translate\"\n placement=\"bottom\"\n [adaptivePosition]=\"false\"\n [delay]=\"500\"\n data-cy=\"c8y-alarms-interval-refresh--toggle-countdown\"\n >\n <input\n type=\"checkbox\"\n data-cy=\"c8y-alarms-interval-toggle\"\n formControlName=\"intervalToggle\"\n (click)=\"trackUserClickOnIntervalToggle($event.target)\"\n />\n <c8y-countdown-interval\n *ngIf=\"isIntervalToggleEnabled\"\n [countdownInterval]=\"toggleIntervalForm.value.refreshInterval\"\n (countdownEnded)=\"onCountdownEnded.emit()\"\n ></c8y-countdown-interval>\n <i\n data-cy=\"c8y-alarms-interval-refresh--pause\"\n c8yIcon=\"pause\"\n *ngIf=\"!isIntervalToggleEnabled\"\n ></i>\n </label>\n <div\n class=\"c8y-select-wrapper\"\n *ngIf=\"!isDisabled\"\n >\n <select\n class=\"form-control text-12\"\n [attr.aria-label]=\"'Refresh interval in seconds' | translate\"\n [tooltip]=\"'Refresh interval in seconds' | translate\"\n placement=\"bottom\"\n [adaptivePosition]=\"false\"\n [delay]=\"500\"\n [container]=\"'body'\"\n formControlName=\"refreshInterval\"\n data-cy=\"c8y-alarms-interval-refresh--selector\"\n >\n <option\n [disabled]=\"isDisabled\"\n *ngFor=\"let refreshInterval of refreshIntervalsInMilliseconds\"\n [ngValue]=\"refreshInterval\"\n [attr.data-cy]=\"'c8y-interval-' + refreshInterval\"\n >\n {{ SECONDS_UNTIL_REFRESH | translate: { seconds: refreshInterval / 1000 } }}\n </option>\n </select>\n <span></span>\n </div>\n <div class=\"input-group-btn\">\n <button\n class=\"btn btn-default\"\n style=\"border-left: 0\"\n [attr.aria-label]=\"'Refresh' | translate\"\n [tooltip]=\"'Refresh' | translate\"\n placement=\"bottom\"\n type=\"button\"\n [adaptivePosition]=\"false\"\n [delay]=\"500\"\n [disabled]=\"isDisabled || (alarmsListLoading$ | async)\"\n (click)=\"onCountdownEnded.emit()\"\n data-cy=\"c8y-alarms-interval-refresh--btn\"\n >\n <i\n c8yIcon=\"refresh\"\n [ngClass]=\"{ 'icon-spin': alarmsListLoading$ | async }\"\n ></i>\n </button>\n </div>\n </div>\n</form>\n" }]
}], ctorParameters: () => [{ type: i1$2.FormBuilder }, { type: AlarmsViewService }], propDecorators: { isDisabled: [{
type: Input
}], alarmsListLoading$: [{
type: Input
}], isIntervalToggleEnabled: [{
type: Input
}], onCountdownEnded: [{
type: Output
}], countdownIntervalComponent: [{
type: ViewChild,
args: [CountdownIntervalComponent]
}] } });
class AlarmsListComponent {
constructor(activatedRoute, alarmsViewService, contextRouteService, router) {
this.activatedRoute = activatedRoute;
this.alarmsViewService = alarmsViewService;
this.contextRouteService = contextRouteService;
this.router = router;
this.alarmBadgeTooltip = gettext('Number of occurrences`number of occurrences of alarm`. First occurrence {{ alarmFirstOccurrenceTime }} (device time).');
this.alarmLastOccurrenceLabel = gettext('Last occurrence of this alarm (device time).');
this.hasPermissions = true;
/**
* Input property for the currently applied type filters.
*/
this.typeFilters = [];
/**
* Input property for receiving load more mode.
*/
this.loadMoreMode = 'hidden';
/**
* Defines options, how the alarm list should be navigated if a user
* clicks on an alarm.
*/
this.navigationOptions = {
allowNavigationToAlarmsView: true,
alwaysNavigateToAllAlarms: false,
includeClearedQueryParams: false,
queryParamsHandling: 'merge'
};
/**
* Controls the visibility of the loading bar
* When set to `false`, the alarm list is displayed. When set to `true`, the opacity of alarms list is changed and a loading bar is shown.
*/
this.isInitialLoading = false;
/**
* Controls the visibility and functionality of some components
* When set to `true`, means the list is displayed in a split view layout:
* the list on the first column and the selected record detail on the second column (the cockpit
* alarms view for example)
* When set to false, the list is displayed as a standalone component, opening the detail will
* redirect to the alarms
*/
this.splitView = false;
/**
* Emits an instance of a selected alarm when one is chosen from the list.
*/
this.onSelectedAlarm = new EventEmitter();
/**
* Emits a boolean value indicating the scrolling state: true when the user starts scrolling, and false when the user reaches the top of the list.
*/
this.onScrollingStateChange = new EventEmitter();
/**
* Current alarm or last alarm marked as active by the routerLinkActive directive.
*/
this.activeAlarm$ = new BehaviorSubject(null);
this.activeChildParam$ = new Observable();
this.isScrolling = false;
/**
* Determines whether the c8y-loading component should be displayed.
* The loading component is shown when no alarms are displayed in the view or when the request is initial,
* as we don't want to see empty space on alarm list during loading.
*/
this.isEmptyListLoading = true;
this.alertAggregator = new DynamicComponentAlertAggregator();
this.mapAlarmLink = pipe(map((alarms) => alarms.map((alarm) => {
alarm.link = this.getRouterLink(alarm);
return alarm;
})));
this.destroy$ = new Subject();
this.HIDE_INTERVAL_COUNTDOWN_SCROLL = 50;
this.verifyIfFiltersMatchingAlarm();
}
/**
* Handles the change of the active route.
*
* @param isActive - A boolean indicating whether the route is active or not.
* @param scrollAnchor - The ListItemComponent used as a scroll anchor.
* @param alarm - The IAlarm object representing the active alarm.
*/
activeRouteChanged(isActive, scrollAnchor, alarm) {
if (isActive) {
scrollAnchor.element.nativeElement.scrollIntoView({
behavior: 'smooth',
block: 'nearest'
});
this.activeAlarm$.next(alarm);
}
}
ngOnChanges(changes) {
if (this.alarms && changes.alarms) {
this.activeAlarm$.next(null);
this.isEmptyListLoading = !this.alarms?.data?.length;
}
if (changes.hasPermissions?.currentValue === false) {
this.alertAggregator.addAlerts(new DynamicComponentAlert({
type: 'system',
text: gettext("You don't have permission to view alarms.")
}));
}
}
ngAfterViewInit() {
if (this.alarmsViewService.isIntervalRefresh()) {
const scrollElement = this.innerScrollWrapper.nativeElement;
fromEvent(scrollElement, 'scroll')
.pipe(takeUntil(this.destroy$), debounceTime(300))
.subscribe((event) => {
const target = event.target;
this.isScrolling = this.shouldCountdownIntervalBeHidden(target);
this.onScrollingStateChange.emit(this.isScrolling);
});
}
}
ngOnDestroy() {
this.destroy$.next();
this.destroy$.complete();
}
onAlarmOpen(alarm) {
this.onSelectedAlarm.emit(alarm);
}
getRouterLink(alarm) {
if (this.navigationOptions.alwaysNavigateToAllAlarms) {
return this.alarmsViewService.getRouterLink(null, alarm);
}
const contextData = this.contextRouteService.getContextData(this.activatedRoute);
return this.alarmsViewService.getRouterLink(contextData, alarm);
}
shouldCountdownIntervalBeHidden(target) {
const scrollTopPixels = target.scrollTop;
return scrollTopPixels > this.HIDE_INTERVAL_COUNTDOWN_SCROLL;
}
verifyIfFiltersMatchingAlarm() {
this.activeChildParam$ = this.router.events.pipe(filter(e => e instanceof NavigationEnd && this.activatedRoute.children.length > 0), switchMap(() => this.activatedRoute.children[0].params), map(params => params.id), distinctUntilChanged(), shareReplay(), takeUntil(this.destroy$));
// done to get the first navigation
this.activeChildParam$.subscribe();
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AlarmsListComponent, deps: [{ token: i1.ActivatedRoute }, { token: AlarmsViewService }, { token: i3.ContextRouteService }, { token: i1.Router }], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.14", type: AlarmsListComponent, isStandalone: true, selector: "c8y-alarms-list", inputs: { alarms: "alarms", hasPermissions: "hasPermissions", typeFilters: "typeFilters", loadMoreMode: "loadMoreMode", navigationOptions: "navigationOptions", isInitialLoading: "isInitialLoading", splitView: "splitView" }, outputs: { onSelectedAlarm: "onSelectedAlarm", onScrollingStateChange: "onScrollingStateChange" }, viewQueries: [{ propertyName: "innerScrollWrapper", first: true, predicate: ["scrollWrapper"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div\n class=\"inner-scroll\"\n [ngClass]=\"{ 'split-view__list bg-level-1': splitView, 'bg-component': !splitView }\"\n data-cy=\"c8y-alarms-list\"\n #scrollWrapper\n>\n <div\n class=\"flex-wrap flex-no-shrink sticky-top m-b-16\"\n [ngClass]=\"{\n 'separator-bottom card-header p-b-0': splitView,\n 'd-flex fit-w separator-top-bottom widget-bar p-l-16 p-r-16':\n !splitView && navigationOptions.allowNavigationToAlarmsView\n }\"\n >\n <div\n class=\"h4 card-title\"\n *ngIf=\"splitView\"\n >\n {{ 'Alarms list' | translate }}\n </div>\n <div\n [ngClass]=\"{ 'fit-w d-flex a-i-center gap-16': !splitView, 'fit-h-20 m-l-auto': splitView }\"\n >\n <ng-content></ng-content>\n </div>\n <!-- Loading -->\n <div\n class=\"fit-w overflow-hidden\"\n [ngClass]=\"{ 'p-t-16': splitView }\"\n >\n <div\n class=\"loading-bar\"\n data-cy=\"c8y-alarms-list--loading-bar\"\n style=\"z-index: 101\"\n [ngClass]=\"{ active: isInitialLoading && !isEmptyListLoading }\"\n ></div>\n </div>\n\n <div\n class=\"alert alert-warning\"\n role=\"alert\"\n translate\n *ngIf=\"\n !isEmptyListLoading &&\n (activeChildParam$ | async) &&\n (activeAlarm$ | async)?.id !== (activeChildParam$ | async)\n \"\n >\n The selected alarm is not currently in the list, change your filter.\n </div>\n </div>\n <c8y-list-group\n class=\"p-r-16 interactive\"\n [ngStyle]=\"{ opacity: isInitialLoading && !isEmptyListLoading ? 0.2 : 1 }\"\n style=\"transition: opacity 0.15s linear\"\n data-cy=\"c8y-alarms-list--group\"\n >\n <c8y-li-timeline\n class=\"pointer\"\n role=\"button\"\n data-cy=\"c8y-alarms-list--timeline-repeat\"\n *c8yFor=\"let alarm of alarms; let i = index; pipe: mapAlarmLink; loadMore: loadMoreMode\"\n [routerLink]=\"navigationOptions.allowNavigationToAlarmsView ? alarm.link : null\"\n routerLinkActive=\"active\"\n [queryParamsHandling]=\"navigationOptions.queryParamsHandling\"\n (isActiveChange)=\"activeRouteChanged($event, liScrollAnchor, alarm)\"\n (click)=\"onAlarmOpen(alarm)\"\n [queryParams]=\"\n navigationOptions.includeClearedQueryParams\n ? { showCleared: alarm.status === 'CLEARED' }\n : {}\n \"\n >\n <span\n [attr.aria-label]=\"alarmLastOccurrenceLabel | translate\"\n [tooltip]=\"alarmLastOccurrenceLabel | translate\"\n placement=\"right\"\n data-cy=\"c8y-alarms-list--last-occurrence-date\"\n container=\"body\"\n [delay]=\"500\"\n >\n {{ alarm.time | c8yDate: 'mediumDate' }}\n {{ alarm.time | c8yDate: 'mediumTime' }}\n </span>\n <c8y-li\n style=\"scroll-margin-top: 56px\"\n #liScrollAnchor\n >\n <c8y-li-icon class=\"a-s-start\">\n <div class=\"alarm-icons\">\n <c8y-alarms-icon [typeFilters]=\"typeFilters\" [alarm]=\"alarm\"></c8y-alarms-icon>\n </div>\n <button\n class=\"btn-clean text-center\"\n data-cy=\"c8y-alarms-list--badge\"\n [attr.aria-label]=\"\n alarmBadgeTooltip\n | translate\n : { alarmFirstOccurrenceTime: alarm.firstOccurrenceTime | c8yDate: 'medium' }\n \"\n [tooltip]=\"\n alarmBadgeTooltip\n | translate\n : { alarmFirstOccurrenceTime: alarm.firstOccurrenceTime | c8yDate: 'medium' }\n \"\n placement=\"right\"\n container=\"body\"\n type=\"button\"\n *ngIf=\"alarm.firstOccurrenceTime\"\n (click)=\"$event.stopPropagation()\"\n [delay]=\"500\"\n >\n <span\n class=\"badge badge-info\"\n *ngIf=\"alarm.count > 1\"\n >\n {{ alarm.count }}\n </span>\n </button>\n </c8y-li-icon>\n <c8y-li-body class=\"a-s-stretch\">\n <div class=\"d-flex a-i-start fit-h\">\n <div class=\"min-width-0 flex-grow\">\n <p class=\"text-truncate-wrap p-b-4\" data-cy=\"c8y-alarms-list--alarm-text\">\n {{ alarm.text | translate }}\n </p>\n <div class=\"d-flex\">\n <p\n class=\"small text-muted text-truncate flex-grow\"\n [title]=\"alarm.source.name\"\n data-cy=\"c8y-alarms-list--alarm-source-name\"\n >\n <i [c8yIcon]=\"'exchange'\"></i>\n {{ alarm.source.name }}\n </p>\n <div class=\"d-flex\">\n <div\n [title]=\"item.title | translate\"\n *ngFor=\"let item of alarm | alarmListIndicator | async\"\n >\n <i\n [class]=\"item.class\"\n [c8yIcon]=\"item.icon\"\n ></i>\n </div>\n </div>\n </div>\n </div>\n </div>\n </c8y-li-body>\n </c8y-li>\n </c8y-li-timeline>\n <c8y-loading\n data-cy=\"c8y-alarms-list--c8y-loading\"\n *ngIf=\"isInitialLoading && isEmptyListLoading\"></c8y-loading>\n <div\n class=\"p-relative p-l-24\"\n *ngIf=\"isEmptyListLoading && !isInitialLoading\"\n >\n <c8y-ui-empty-state\n [icon]=\"'c8y-alert-idle'\"\n [title]=\"'No alarms to display.' | translate\"\n data-cy=\"c8y-alarms-list--empty-state\"\n *ngIf=\"hasPermissions; else alertsA\"\n >\n <p c8y-guide-docs>\n <small\n translate\n ngNonBindable\n >\n Find out more in the\n <a\n c8y-guide-href=\"/docs/device-management-application/monitoring-and-controlling-devices/#working-with-alarms\"\n >\n user documentation\n </a>\n .\n </small>\n </p>\n </c8y-ui-empty-state>\n </div>\n </c8y-list-group>\n</div>\n\n<ng-template #alertsA>\n <c8y-dynamic-component-alerts [alerts]=\"alertAggregator\"></c8y-dynamic-component-alerts>\n</ng-template>\n", dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: C8yTranslateDirective, selector: "[translate],[ngx-translate]" }, { kind: "component", type: ListGroupComponent, selector: "c8y-list-group" }, { kind: "directive", type: NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: 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: ListItemTimelineComponent, selector: "c8y-list-item-timeline, c8y-li-timeline" }, { kind: "directive", type: RouterLinkActive, selector: "[routerLinkActive]", inputs: ["routerLinkActiveOptions", "ariaCurrentWhenActive", "routerLinkActive"], outputs: ["isActiveChange"], exportAs: ["routerLinkActive"] }, { kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "directive", type: TooltipDirective, selector: "[tooltip], [tooltipHtml]", inputs: ["adaptivePosition", "tooltip", "placement", "triggers", "container", "containerClass", "boundariesElement", "isOpen", "isDisabled", "delay", "tooltipHtml", "tooltipPlacement", "tooltipIsOpen", "tooltipEnable", "tooltipAppendToBody", "tooltipAnimation", "tooltipClass", "tooltipContext", "tooltipPopupDelay", "tooltipFadeDuration", "tooltipTrigger"], outputs: ["tooltipChange", "onShown", "onHidden", "tooltipStateChanged"], exportAs: ["bs-tooltip"] }, { kind: "component", type: ListItemComponent, selector: "c8y-list-item, c8y-li", inputs: ["active", "highlighted", "emptyActions", "dense", "collapsed", "selectable"], outputs: ["collapsedChange"] }, { kind: "component", type: ListItemIconComponent, selector: "c8y-list-item-icon, c8y-li-icon", inputs: ["icon", "status"] }, { kind: "component", type: AlarmsIconComponent, selector: "c8y-alarms-icon", inputs: ["alarm", "typeFilters"] }, { kind: "component", type: ListItemBodyComponent, selector: "c8y-list-item-body, c8y-li-body", inputs: ["body"] }, { kind: "directive", type: IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "directive", type: NgFor, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "component", type: LoadingComponent, selector: "c8y-loading", inputs: ["layout", "progress", "message"] }, { kind: "component", type: EmptyStateComponent, selector: "c8y-ui-empty-state", inputs: ["icon", "title", "subtitle", "horizontal"] }, { kind: "component", type: GuideDocsComponent, selector: "[c8y-guide-docs]" }, { kind: "directive", type: GuideHrefDirective, selector: "[c8y-guide-href]", inputs: ["c8y-guide-href"] }, { kind: "component", type: DynamicComponentAlertsComponent, selector: "c8y-dynamic-component-alerts", inputs: ["alerts"] }, { kind: "pipe", type: C8yTranslatePipe, name: "translate" }, { kind: "pipe", type: AsyncPipe, name: "async" }, { kind: "pipe", type: DatePipe, name: "c8yDate" }, { kind: "pipe", type: AlarmListIndicatorPipe, name: "alarmListIndicator" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AlarmsListComponent, decorators: [{
type: Component,
args: [{ selector: 'c8y-alarms-list', imports: [
NgClass,
NgIf,
C8yTranslateDirective,
ListGroupComponent,
NgStyle,
ForOfDirective,
ListItemTimelineComponent,
RouterLinkActive,
RouterLink,
TooltipDirective,
ListItemComponent,
ListItemIconComponent,
AlarmsIconComponent,
ListItemBodyComponent,
IconDirective,
NgFor,
LoadingComponent,
EmptyStateComponent,
GuideDocsComponent,
GuideHrefDirective,
DynamicComponentAlertsComponent,
C8yTranslatePipe,
AsyncPipe,
DatePipe,
AlarmListIndicatorPipe
], template: "<div\n class=\"inner-scroll\"\n [ngClass]=\"{ 'split-view__list bg-level-1': splitView, 'bg-component': !splitView }\"\n data-cy=\"c8y-alarms-list\"\n #scrollWrapper\n>\n <div\n class=\"flex-wrap flex-no-shrink sticky-top m-b-16\"\n [ngClass]=\"{\n 'separator-bottom card-header p-b-0': splitView,\n 'd-flex fit-w separator-top-bottom widget-bar p-l-16 p-r-16':\n !splitView && navigationOptions.allowNavigationToAlarmsView\n }\"\n >\n <div\n class=\"h4 card-title\"\n *ngIf=\"splitView\"\n >\n {{ 'Alarms list' | translate }}\n </div>\n <div\n [ngClass]=\"{ 'fit-w d-flex a-i-center gap-16': !splitView, 'fit-h-20 m-l-auto': splitView }\"\n >\n <ng-content></ng-content>\n </div>\n <!-- Loading -->\n <div\n class=\"fit-w overflow-hidden\"\n [ngClass]=\"{ 'p-t-16': splitView }\"\n >\n <div\n class=\"loading-bar\"\n data-cy=\"c8y-alarms-list--loading-bar\"\n style=\"z-index: 101\"\n [ngClass]=\"{ active: isInitialLoading && !isEmptyListLoading }\"\n ></div>\n </div>\n\n <div\n class=\"alert alert-warning\"\n role=\"alert\"\n translate\n *ngIf=\"\n !isEmptyListLoading &&\n (activeChildParam$ | async) &&\n (activeAlarm$ | async)?.id !== (activeChildParam$ | async)\n \"\n >\n The selected alarm is not currently in the list, change your filter.\n </div>\n </div>\n <c8y-list-group\n class=\"p-r-16 interactive\"\n [ngStyle]=\"{ opacity: isInitialLoading && !isEmptyListLoading ? 0.2 : 1 }\"\n style=\"transition: opacity 0.15s linear\"\n data-cy=\"c8y-alarms-list--group\"\n >\n <c8y-li-timeline\n class=\"pointer\"\n role=\"button\"\n data-cy=\"c8y-alarms-list--timeline-repeat\"\n *c8yFor=\"let alarm of alarms; let i = index; pipe: mapAlarmLink; loadMore: loadMoreMode\"\n [routerLink]=\"navigationOptions.allowNavigationToAlarmsView ? alarm.link : null\"\n routerLinkActive=\"active\"\n [queryParamsHandling]=\"navigationOptions.queryParamsHandling\"\n (isActiveChange)=\"activeRouteChanged($event, liScrollAnchor, alarm)\"\n (click)=\"onAlarmOpen(alarm)\"\n [queryParams]=\"\n navigationOptions.includeClearedQueryParams\n ? { showCleared: alarm.status === 'CLEARED' }\n : {}\n \"\n >\n <span\n [attr.aria-label]=\"alarmLastOccurrenceLabel | translate\"\n [tooltip]=\"alarmLastOccurrenceLabel | translate\"\n placement=\"right\"\n data-cy=\"c8y-alarms-list--last-occurrence-date\"\n container=\"body\"\n [delay]=\"500\"\n >\n {{ alarm.time | c8yDate: 'mediumDate' }}\n {{ alarm.time | c8yDate: 'mediumTime' }}\n </span>\n <c8y-li\n style=\"scroll-margin-top: 56px\"\n #liScrollAnchor\n >\n <c8y-li-icon class=\"a-s-start\">\n <div class=\"alarm-icons\">\n <c8y-alarms-icon [typeFilters]=\"typeFilters\" [alarm]=\"alarm\"></c8y-alarms-icon>\n </div>\n <button\n class=\"btn-clean text-center\"\n data-cy=\"c8y-alarms-list--badge\"\n [attr.aria-label]=\"\n alarmBadgeTooltip\n | translate\n : { alarmFirstOccurrenceTime: alarm.firstOccurrenceTime | c8yDate: 'medium' }\n \"\n [tooltip]=\"\n alarmBadgeTooltip\n | translate\n : { alarmFirstOccurrenceTime: alarm.firstOccurrenceTime | c8yDate: 'medium' }\n \"\n placement=\"right\"\n container=\"body\"\n type=\"button\"\n *ngIf=\"alarm.firstOccurrenceTime\"\n (click)=\"$event.stopPropagation()\"\n [delay]=\"500\"\n >\n <span\n class=\"badge badge-info\"\n *ngIf=\"alarm.count > 1\"\n >\n {{ alarm.count }}\n </span>\n </button>\n </c8y-li-icon>\n <c8y-li-body class=\"a-s-stretch\">\n <div class=\"d-flex a-i-start fit-h\">\n <div class=\"min-width-0 flex-grow\">\n <p class=\"text-truncate-wrap p-b-4\" data-cy=\"c8y-alarms-list--alarm-text\">\n {{ alarm.text | translate }}\n </p>\n <div class=\"d-flex\">\n <p\n class=\"small text-muted text-truncate flex-grow\"\n [title]=\"alarm.source.name\"\n data-cy=\"c8y-alarms-list--alarm-source-name\"\n >\n <i [c8yIcon]=\"'exchange'\"></i>\n {{ alarm.source.name }}\n </p>\n <div class=\"d-flex\">\n <div\n [title]=\"item.title | translate\"\n *ngFor=\"let item of alarm | alarmListIndicator | async\"\n >\n <i\n [class]=\"item.class\"\n [c8yIcon]=\"item.icon\"\n ></i>\n </div>\n </div>\n </div>\n </div>\n </div>\n </c8y-li-body>\n </c8y-li>\n </c8y-li-timeline>\n <c8y-loading\n data-cy=\"c8y-alarms-list--c8y-loading\"\n *ngIf=\"isInitialLoading && isEmptyListLoading\"></c8y-loading>\n <div\n class=\"p-relative p-l-24\"\n *ngIf=\"isEmptyListLoading && !isInitialLoading\"\n >\n <c8y-ui-empty-state\n [icon]=\"'c8y-alert-idle'\"\n [title]=\"'No alarms to display.' | translate\"\n data-cy=\"c8y-alarms-list--empty-state\"\n *ngIf=\"hasPermissions; else alertsA\"\n >\n <p c8y-guide-docs>\n <small\n translate\n ngNonBindable\n >\n Find out more in the\n <a\n c8y-guide-href=\"/docs/device-management-application/monitoring-and-controlling-devices/#working-with-alarms\"\n >\n user documentation\n </a>\n .\n </small>\n </p>\n </c8y-ui-empty-state>\n </div>\n </c8y-list-group>\n</div>\n\n<ng-template #alertsA>\n <c8y-dynamic-component-alerts [alerts]=\"alertAggregator\"></c8y-dynamic-component-alerts>\n</ng-template>\n" }]
}], ctorParameters: () => [{ type: i1.ActivatedRoute }, { type: AlarmsViewService }, { type: i3.ContextRouteService }, { type: i1.Router }], propDecorators: { alarms: [{
type: Input
}], hasPermissions: [{
type: Input
}], typeFilters: [{
type: Input
}], loadMoreMode: [{
type: Input
}], navigationOptions: [{
type: Input
}], isInitialLoading: [{
type: Input
}], splitView: [{
type: Input
}], onSelectedAlarm: [{
type: Output
}], onScrollingStateChange: [{
type: Output
}], innerScrollWrapper: [{
type: ViewChild,
args: ['scrollWrapper']
}] } });
class AlarmsDateFilterComponent {
constructor(formBuilder, router, activatedRoute, alarmsViewService) {
this.formBuilder = formBuilder;
this.router = router;
this.activatedRoute = activatedRoute;
this.alarmsViewService = alarmsViewService;
this.INTERVALS = INTERVALS_EXTENDED;
this.INTERVAL_TITLES = INTERVAL_TITLES_EXTENDED;
this.DATE_FORMAT = 'short';
this.DEFAULT_INTERVAL = 'none';
this.updateQueryParams = true;
this.noFilterLabel = gettext('No date filter');
this.destroy$ = new Subject();
this.dateFilterChange = new EventEmitter();
// eslint-disable-next-line @typescript-eslint/no-empty-function
this.onTouched = () => { };
}
ngOnInit() {
const context = this.getDefaultContext();
this.form = this.createForm(context);
this.date = [
this.form.value.currentDateContextFromDate,
this.form.value.currentDateContextToDate
];
this.activatedRoute.queryParams.pipe(take(1), takeUntil$1(this.destroy$)).subscribe(params => {
this.showCleared = params.showCleared === 'true';
this.severityOptions = {
[Severity.CRITICAL]: params.critical === 'true',
[Severity.MAJOR]: params.major === 'true',
[Severity.MINOR]: params.minor === 'true',
[Severity.WARNING]: params.warning === 'true'
};
if (params.typeFilters) {
this.typeFilters = params.typeFilters;
}
if (!params.interval) {
return;
}
if (params.interval !== 'custom') {
this.updateDateTime(params.interval);
}
else {
this.form.patchValue({
currentDateContextInterval: params.interval,
temporaryUserSelectedFromDate: params.lastUpdatedFrom,
temporaryUserSelectedToDate: params.createdTo
});
this.date = [params.lastUpdatedFrom, params.createdTo];
}
});
this.subscribeToIntervalChange();
}
ngOnDestroy() {
this.destroy$.next();
this.destroy$.complete();
}
applyDateFilter() {
const combinedFormEvent = {
showCleared: this.showCleared,
severityOptions: this.severityOptions,
typeFilters: this.typeFilters,
interval: this.form.value.currentDateContextInterval,
selectedDates: [
new Date(this.form.value.temporaryUserSelectedFromDate),
new Date(this.form.value.temporaryUserSelectedToDate)
]
};
// needed for custom interval
this.date = [
this.form.value.temporaryUserSelectedFromDate,
this.form.value.temporaryUserSelectedToDate
];
this.router.navigate([], {
relativeTo: this.activatedRoute,
queryParams: {
interval: this.form.value.currentDateContextInterval,
lastUpdatedFrom: combinedFormEvent.selectedDates[0].toISOString(),
createdTo: combinedFormEvent.selectedDates[1].toISOString()
},
queryParamsHandling: 'merge'
});
this.dateFilterChange.emit(combinedFormEvent);
}
writeValue(value) {
if (value) {
this.form.patchValue({
currentDateContextFromDate: typeof value[0] === 'string' ? value[0] : value[0].toISOString(),
currentDateContextToDate: typeof value[1] === 'string' ? value[1] : value[1].toISOString()
});
}
}
registerOnChange(fn) {
this.onChange = fn;
}
registerOnTouched(onTouched) {
this.onTouched = onTouched;
}
updateDateTime(interval) {
const date = this.alarmsViewService.getDateTimeContextByInterval(interval);
if (this.dropdown) {
this.dropdown.isOpen = false;
}
this.date = date.map(d => d.toISOString());
this.form.patchValue({
temporaryUserSelectedFromDate: date[0].toISOString(),
temporaryUserSelectedToDate: date[1].toISOString(),
currentDateContextInterval: interval
}, { emitEvent: false });
this.applyDateFilter();
}
getDefaultContext() {
return {
date: this.alarmsViewService.getDateTimeContextByInterval(this.DEFAULT_INTERVAL),
interval: this.DEFAULT_INTERVAL
};
}
subscribeToIntervalChange() {
this.form.controls.currentDateContextInterval.valueChanges
.pipe(takeUntil$1(this.destroy$))
.subscribe(interval => {
if (interval === 'custom') {
this.form.patchValue({
temporaryUserSelectedFromDate: this.form.controls.temporaryUserSelectedFromDate.value === new Date(0).toISOString()
? this.form.controls.currentDateContextToDate.value
: this.form.controls.temporaryUserSelectedFromDate.value,
currentDateContextInterval: interval
}, { emitEvent: false });
return;
}
this.updateDateTime(interval);
});
}
createForm(context) {
return this.formBuilder.group({
temporaryUserSelectedFromDate: context.date[0].toISOString(),
temporaryUserSelectedToDate: context.date[1].toISOString(),
currentDateContextFromDate: context.date[0].toISOString(),
currentDateContextToDate: context.date[1].toISOString(),
currentDateContextInterval: context.interval || 'custom'
});
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AlarmsDateFilterComponent, deps: [{ token: i1$2.FormBuilder }, { token: i1.Router }, { token: i1.ActivatedRoute }, { token: AlarmsViewService }], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.14", type: AlarmsDateFilterComponent, isStandalone: true, selector: "c8y-alarms-date-filter", inputs: { DEFAULT_INTERVAL: "DEFAULT_INTERVAL", updateQueryParams: "updateQueryParams", date: "date" }, outputs: { dateFilterChange: "dateFilterChange" }, providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => AlarmsDateFilterComponent),
multi: true
}
], viewQueries: [{ propertyName: "dropdown", first: true, predicate: BsDropdownDirective, descendants: true }], ngImport: i0, template: "<form\n class=\"d-flex gap-16 p-l-xs-16 p-r-xs-16 m-t-xs-8 m-b-xs-8\"\n [formGroup]=\"form\"\n>\n <div\n class=\"dropdown flex-grow\"\n c8yDropdownDirection\n #dropDirection=\"bs-dropdown\"\n dropdown\n [insideClick]=\"true\"\n >\n <button\n class=\"dropdown-toggle form-control l-h-tight d-flex a-i-center\"\n attr.aria-label=\"{{\n (form.value.currentDateContextInterval === 'none'\n ? noFilterLabel\n : (date[0] | c8yDate: DATE_FORMAT) + ' \u2014 ' + (date[1] | c8yDate: DATE_FORMAT)\n ) | translate\n }}\"\n tooltip=\"{{\n (form.value.currentDateContextInterval === 'none'\n ? noFilterLabel\n : (date[0] | c8yDate: DATE_FORMAT) + ' \u2014 ' + (date[1] | c8yDate: DATE_FORMAT)\n ) | translate\n }}\"\n placement=\"top\"\n container=\"body\"\n data-cy=\"alarms-date-filter--date-picker-dropdown-button\"\n [adaptivePosition]=\"false\"\n [delay]=\"500\"\n dropdownToggle\n >\n <i\n class=\"m-r-4\"\n c8yIcon=\"schedule1\"\n ></i>\n <div class=\"d-col text-left fit-w\">\n <span\n class=\"text-12\"\n data-cy=\"widget-time-context--selected-interval\"\n >\n {{ INTERVAL_TITLES[form.controls.currentDateContextInterval.value] | translate }}\n </span>\n <span\n class=\"text-10 text-muted text-truncate\"\n data-cy=\"alarms-date-filter--selected-time-range\"\n *ngIf=\"form.controls.currentDateContextInterval.value !== 'none'\"\n >\n {{ date[0] | c8yDate: DATE_FORMAT }} \u2014 {{ date[1] | c8yDate: DATE_FORMAT }}\n </span>\n </div>\n <span class=\"caret m-r-16 m-l-4\"></span>\n </button>\n\n <ul\n class=\"dropdown-menu dropdown-menu--date-range\"\n *dropdownMenu\n >\n <c8y-interval-picker\n class=\"d-contents\"\n formControlName=\"currentDateContextInterval\"\n [INTERVALS]=\"INTERVALS\"\n ></c8y-interval-picker>\n\n <ng-container *ngIf=\"form.controls.currentDateContextInterval.value === 'custom'\">\n <div class=\"p-l-16 p-r-16\">\n <c8y-form-group\n class=\"m-b-8\"\n [ngClass]=\"form.controls.temporaryUserSelectedFromDate.errors ? 'has-error' : ''\"\n >\n <label\n [title]=\"'From`date`' | translate\"\n for=\"temporaryUserSelectedFromDate\"\n translate\n >\n From`date`\n </label>\n <c8y-date-time-picker\n id=\"temporaryUserSelectedFromDate\"\n [maxDate]=\"form.value.temporaryUserSelectedToDate\"\n [placeholder]=\"'From`date`' | translate\"\n [formControl]=\"form.controls.temporaryUserSelectedFromDate\"\n [ngClass]=\"form.controls.temporaryUserSelectedFromDate.errors ? 'has-error' : ''\"\n ></c8y-date-time-picker>\n <c8y-messages [show]=\"form.controls.temporaryUserSelectedFromDate.errors\">\n <c8y-message\n name=\"dateAfterRangeMax\"\n [text]=\"'This date is after the latest allowed date.' | translate\"\n ></c8y-message>\n <c8y-message\n name=\"invalidDateTime\"\n [text]=\"'This date is invalid.' | translate\"\n ></c8y-message>\n </c8y-messages>\n </c8y-form-group>\n\n <c8y-form-group\n class=\"m-b-8\"\n [ngClass]=\"form.controls.temporaryUserSelectedToDate.errors ? 'has-error' : ''\"\n >\n <label\n [title]=\"'To`date`' | translate\"\n for=\"temporaryUserSelectedToDate\"\n translate\n >\n To`date`\n </label>\n <c8y-date-time-picker\n id=\"temporaryUserSelectedToDate\"\n [minDate]=\"form.value.temporaryUserSelectedFromDate\"\n [placeholder]=\"'To`date`' | translate\"\n [formControl]=\"form.controls.temporaryUserSelectedToDate\"\n [ngClass]=\"form.controls.temporaryUserSelectedToDate.errors ? 'has-error' : ''\"\n ></c8y-date-time-picker>\n <c8y-messages [show]=\"form.controls.temporaryUserSelectedToDate.errors\">\n <c8y-message\n name=\"dateBeforeRangeMin\"\n [text]=\"'This date is before the earliest allowed date.' | translate\"\n ></c8y-message>\n <c8y-message\n name=\"invalidDateTime\"\n [text]=\"'This date is invalid.' | translate\"\n ></c8y-message>\n </c8y-messages>\n </c8y-form-group>\n </div>\n\n <div class=\"p-16 d-flex gap-8 separator-top\">\n <button\n class=\"btn btn-primary btn-sm flex-grow\"\n title=\"{{ 'Apply' | translate }}\"\n type=\"button\"\n (click)=\"applyDateFilter(); dropdown.isOpen = false\"\n [disabled]=\"(form.pristine && form.untouched) || form.invalid\"\n translate\n >\n Apply\n </button>\n </div>\n </ng-container>\n </ul>\n </div>\n</form>\n", dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$2.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$2.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "directive", type: BsDropdownDirective, selector: "[bsDropdown], [dropdown]", inputs: ["placement", "triggers", "container", "dropup", "autoClose", "isAnimated", "insideClick", "isDisabled", "isOpen"], outputs: ["isOpenChange", "onShown", "onHidden"], exportAs: ["bs-dropdown"] }, { kind: "directive", type: DropdownDirectionDirective, selector: "[dropdown][c8yBsDropdownDirection],[dropdown][c8yDropdownDirection]" }, { kind: "directive", type: BsDropdownToggleDirective, selector: "[bsDropdownToggle],[dropdownToggle]", exportAs: ["bs-dropdown-toggle"] }, { kind: "directive", type: TooltipDirective, selector: "[tooltip], [tooltipHtml]", inputs: ["adaptivePosition", "tooltip", "placement", "triggers", "container", "containerClass", "boundariesElement", "isOpen", "isDisabled", "delay", "tooltipHtml", "tooltipPlacement", "tooltipIsOpen", "tooltipEnable", "tooltipAppendToBody", "tooltipAnimation", "tooltipClass", "tooltipContext", "tooltipPopupDelay", "tooltipFadeDuration", "tooltipTrigger"], outputs: ["tooltipChange", "onShown", "onHidden", "tooltipStateChanged"], exportAs: ["bs-tooltip"] }, { kind: "directive", type: IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: BsDropdownMenuDirective, selector: "[bsDropdownMenu],[dropdownMenu]", exportAs: ["bs-dropdown-menu"] }, { kind: "component", type: IntervalPickerComponent, selector: "c8y-interval-picker", inputs: ["INTERVALS"] }, { kind: "component", type: FormGroupComponent, selector: "c8y-form-group", inputs: ["hasError", "hasWarning", "hasSuccess", "novalidation", "status"] }, { kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: C8yTranslateDirective, selector: "[translate],[ngx-translate]" }, { kind: "component", type: DateTimePickerComponent, selector: "c8y-date-time-picker", inputs: ["minDate", "maxDate", "placeholder", "dateInputFormat", "adaptivePosition", "size", "dateType", "config"], outputs: ["onDateSelected"] }, { kind: "component", type: MessagesComponent, selector: "c8y-messages", inputs: ["show", "defaults", "helpMessage"] }, { kind: "directive", type: MessageDirective, selector: "c8y-message", inputs: ["name", "text"] }, { kind: "pipe", type: C8yTranslatePipe, name: "translate" }, { kind: "pipe", type: DatePipe, name: "c8yDate" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AlarmsDateFilterComponent, decorators: [{
type: Component,
args: [{ selector: 'c8y-alarms-date-filter', providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => AlarmsDateFilterComponent),
multi: true
}
], imports: [
FormsModule,
ReactiveFormsModule,
BsDropdownDirective,
DropdownDirectionDirective,
BsDropdownToggleDirective,
TooltipDirective,
IconDirective,
NgIf,
BsDropdownMenuDirective,
IntervalPickerComponent,
FormGroupComponent,
NgClass,
C8yTranslateDirective,
DateTimePickerComponent,
MessagesComponent,
MessageDirective,
C8yTranslatePipe,
DatePipe
], template: "<form\n class=\"d-flex gap-16 p-l-xs-16 p-r-xs-16 m-t-xs-8 m-b-xs-8\"\n [formGroup]=\"form\"\n>\n <div\n class=\"dropdown flex-grow\"\n c8yDropdownDirection\n #dropDirection=\"bs-dropdown\"\n dropdown\n [insideClick]=\"true\"\n >\n <button\n class=\"dropdown-toggle form-control l-h-tight d-flex a-i-center\"\n attr.aria-label=\"{{\n (form.value.currentDateContextInterval === 'none'\n ? noFilterLabel\n : (date[0] | c8yDate: DATE_FORMAT) + ' \u2014 ' + (date[1] | c8yDate: DATE_FORMAT)\n ) | translate\n }}\"\n tooltip=\"{{\n (form.value.currentDateContextInterval === 'none'\n ? noFilterLabel\n : (date[0] | c8yDate: DATE_FORMAT) + ' \u2014 ' + (date[1] | c8yDate: DATE_FORMAT)\n ) | translate\n }}\"\n placement=\"top\"\n container=\"body\"\n data-cy=\"alarms-date-filter--date-picker-dropdown-button\"\n [adaptivePosition]=\"false\"\n [delay]=\"500\"\n dropdownToggle\n >\n <i\n class=\"m-r-4\"\n c8yIcon=\"schedule1\"\n ></i>\n <div class=\"d-col text-left fit-w\">\n <span\n class=\"text-12\"\n data-cy=\"widget-time-context--selected-interval\"\n >\n {{ INTERVAL_TITLES[form.controls.currentDateContextInterval.value] | translate }}\n </span>\n <span\n class=\"text-10 text-muted text-truncate\"\n data-cy=\"alarms-date-filter--selected-time-range\"\n *ngIf=\"form.controls.currentDateContextInterval.value !== 'none'\"\n >\n {{ date[0] | c8yDate: DATE_FORMAT }} \u2014 {{ date[1] | c8yDate: DATE_FORMAT }}\n </span>\n </div>\n <span class=\"caret m-r-16 m-l-4\"></span>\n </button>\n\n <ul\n class=\"dropdown-menu dropdown-menu--date-range\"\n *dropdownMenu\n >\n <c8y-interval-picker\n class=\"d-contents\"\n formControlName=\"currentDateContextInterval\"\n [INTERVALS]=\"INTERVALS\"\n ></c8y-interval-picker>\n\n <ng-container *ngIf=\"form.controls.currentDateContextInterval.value === 'custom'\">\n <div class=\"p-l-16 p-r-16\">\n <c8y-form-group\n class=\"m-b-8\"\n [ngClass]=\"form.controls.temporaryUserSelectedFromDate.errors ? 'has-error' : ''\"\n >\n <label\n [title]=\"'From`date`' | translate\"\n for=\"temporaryUserSelectedFromDate\"\n translate\n >\n From`date`\n </label>\n <c8y-date-time-picker\n id=\"temporaryUserSelectedFromDate\"\n [maxDate]=\"form.value.temporaryUserSelectedToDate\"\n [placeholder]=\"'From`date`' | translate\"\n [formControl]=\"form.controls.temporaryUserSelectedFromDate\"\n [ngClass]=\"form.controls.temporaryUserSelectedFromDate.errors ? 'has-error' : ''\"\n ></c8y-date-time-picker>\n <c8y-messages [show]=\"form.controls.temporaryUserSelectedFromDate.errors\">\n <c8y-message\n name=\"dateAfterRangeMax\"\n [text]=\"'This date is after the latest allowed date.' | translate\"\n ></c8y-message>\n <c8y-message\n name=\"invalidDateTime\"\n [text]=\"'This date is invalid.' | translate\"\n ></c8y-message>\n </c8y-messages>\n </c8y-form-group>\n\n <c8y-form-group\n class=\"m-b-8\"\n [ngClass]=\"form.controls.temporaryUserSelectedToDate.errors ? 'has-error' : ''\"\n >\n <label\n [title]=\"'To`date`' | translate\"\n for=\"temporaryUserSelectedToDate\"\n translate\n >\n To`date`\n </label>\n <c8y-date-time-picker\n id=\"temporaryUserSelectedToDate\"\n [minDate]=\"form.value.temporaryUserSelectedFromDate\"\n [placeholder]=\"'To`date`' | translate\"\n [formControl]=\"form.controls.temporaryUserSelectedToDate\"\n [ngClass]=\"form.controls.temporaryUserSelectedToDate.errors ? 'has-error' : ''\"\n ></c8y-date-time-picker>\n <c8y-messages [show]=\"form.controls.temporaryUserSelectedToDate.errors\">\n <c8y-message\n name=\"dateBeforeRangeMin\"\n [text]=\"'This date is before the earliest allowed date.' | translate\"\n ></c8y-message>\n <c8y-message\n name=\"invalidDateTime\"\n [text]=\"'This date is invalid.' | translate\"\n ></c8y-message>\n </c8y-messages>\n </c8y-form-group>\n </div>\n\n <div class=\"p-16 d-flex gap-8 separator-top\">\n <button\n class=\"btn btn-primary btn-sm flex-grow\"\n title=\"{{ 'Apply' | translate }}\"\n type=\"button\"\n (click)=\"applyDateFilter(); dropdown.isOpen = false\"\n [disabled]=\"(form.pristine && form.untouched) || form.invalid\"\n translate\n >\n Apply\n </button>\n </div>\n </ng-container>\n </ul>\n </div>\n</form>\n" }]
}], ctorParameters: () => [{ type: i1$2.FormBuilder }, { type: i1.Router }, { type: i1.ActivatedRoute }, { type: AlarmsViewService }], propDecorators: { DEFAULT_INTERVAL: [{
type: Input
}], updateQueryParams: [{
type: Input
}], date: [{
type: Input
}], dateFilterChange: [{
type: Output
}], dropdown: [{
type: ViewChild,
args: [BsDropdownDirective]
}] } });
class AlarmsTypeFilterComponent {
constructor(alarmEventSelectorService, activatedRoute, router, colorService) {
this.alarmEventSelectorService = alarmEventSelectorService;
this.activatedRoute = activatedRoute;
this.router = router;
this.colorService = colorService;
this.possibleFilters = [];
this.activeFilters = [];
this.onFilterChanged = new EventEmitter();
this.customAlarmTypes = [];
this.customAlarmTypeInput = '';
this.queryParamName = 'typeFilter';
this.STORAGE_ACCESS_KEY = 'customAlarmTypes';
this.destroy$ = new Subject();
this.currentQueryParam = '';
}
ngOnInit() {
this.setQueryParameterObservable();
}
async ngOnChanges(changes) {
if (changes.alarms && changes.alarms.currentValue && this.activeFilters.length === 0) {
await this.setPossibleFilters();
this.applyFilterChange();
}
}
setQueryParameterObservable() {
this.activatedRoute.queryParams
.pipe(map$1(params => {
const alarms = this.possibleFilters;
const possibleFilters = this.setActiveAlarmFiltersFromQueryParameter(alarms, params[this.queryParamName]);
return possibleFilters;
}), takeUntil$1(this.destroy$))
.subscribe((possibleFilters) => {
this.possibleFilters = possibleFilters;
this.applyFilterChange();
});
}
ngOnDestroy() {
this.destroy$.next();
this.destroy$.complete();
}
toggleAlarmType(alarmType) {
alarmType.__active = !alarmType.__active;
}
deselect(type) {
const alarmFilter = this.possibleFilters.find(alarm => alarm.filters.type === type.filters.type);
alarmFilter.__active = false;
this.applyFilterChange();
}
deselectAll() {
this.possibleFilters = this.possibleFilters.map(alarm => {
return {
...alarm,
__active: false
};
});
this.applyFilterChange();
}
applyFilterChange() {
const actives = this.possibleFilters.filter((alarmFilter) => alarmFilter.__active);
const newQueryParam = this.getQueryParams(actives);
const hasChanged = newQueryParam !== this.currentQueryParam;
if (hasChanged) {
this.activeFilters = actives;
this.onFilterChanged.emit(this.activeFilters);
this.router.navigate([], {
queryParams: {
typeFilter: newQueryParam || null
},
queryParamsHandling: 'merge'
});
this.currentQueryParam = newQueryParam;
}
}
resetFilters() {
this.possibleFilters.forEach(possibleFilter => {
possibleFilter.__active = this.activeFilters.some((activeFilter) => activeFilter === possibleFilter);
});
}
removeCustomAlarm(alarmDetails) {
this.possibleFilters = this.possibleFilters.filter(filter => filter !== alarmDetails);
this.storeCustomAlarmTypes();
}
confirmWithEnter(event) {
if (event.key === 'Enter') {
this.addCustomAlarmType();
}
}
async addCustomAlarmType() {
if (!this.customAlarmTypeInput) {
return;
}
this.possibleFilters.unshift({
label: this.customAlarmTypeInput,
color: await this.colorService.generateColor(this.customAlarmTypeInput),
filters: {
type: this.customAlarmTypeInput
},
timelineType: 'ALARM',
__active: true,
__target: null
});
this.customAlarmTypeInput = '';
this.storeCustomAlarmTypes();
}
storeCustomAlarmTypes() {
const customTypes = this.possibleFilters.filter((filter) => !filter.__target);
window.localStorage.setItem(this.STORAGE_ACCESS_KEY, JSON.stringify(customTypes));
}
getCustomAlarmTypeFromStorage() {
const types = window.localStorage.getItem(this.STORAGE_ACCESS_KEY);
return types ? JSON.parse(types) : [];
}
async setPossibleFilters() {
const queryParameters = this.activatedRoute.snapshot.queryParamMap.get(this.queryParamName);
const alarmTypesFromCurrentlyShownAlarms = await this.alarmEventSelectorService.getUniqueAlarmsOnly(this.alarms.data);
const customAlarmTypesFromLocalStorage = this.getCustomAlarmTypeFromStorage();
const selectableAlarmTypes = this.setActiveAlarmFiltersFromQueryParameter([...customAlarmTypesFromLocalStorage, ...alarmTypesFromCurrentlyShownAlarms], queryParameters);
this.possibleFilters = selectableAlarmTypes;
}
setActiveAlarmFiltersFromQueryParameter(alarmFilters, filterTypesQuery = '') {
const types = (filterTypesQuery ?? '').split(',');
return alarmFilters.map((alarm) => ({
...alarm,
__active: types.includes(alarm.filters.type)
}));
}
getQueryParams(activeFilters) {
return activeFilters.map(filter => filter.filters.type).join(',');
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AlarmsTypeFilterComponent, deps: [{ token: i1$3.AlarmEventSelectorService }, { token: i1.ActivatedRoute }, { token: i1.Router }, { token: i3.ColorService }], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.14", type: AlarmsTypeFilterComponent, isStandalone: true, selector: "c8y-alarms-type-filter", inputs: { alarms: "alarms", possibleFilters: "possibleFilters", activeFilters: "activeFilters" }, outputs: { onFilterChanged: "onFilterChanged" }, usesOnChanges: true, ngImport: i0, template: "<div class=\"d-flex a-i-center\">\n <div\n class=\"dropdown\"\n title=\"{{ 'Filter by alarm types' | translate }}\"\n dropdown\n #filtersDropdown=\"bs-dropdown\"\n [cdkTrapFocus]=\"filtersDropdown.isOpen\"\n (onHidden)=\"resetFilters()\"\n [insideClick]=\"true\"\n >\n <div class=\"input-group fit-w\">\n <div class=\"form-control d-flex a-i-center inner-scroll\">\n <ng-container *ngIf=\"activeFilters.length > 0; else allTypes\">\n <span\n class=\"tag tag--info chip\"\n style=\"max-width: 150px\"\n *ngFor=\"let filter of activeFilters\"\n >\n <button\n class=\"btn btn-xs btn-clean text-10\"\n title=\"{{ 'Remove' | translate }}\"\n type=\"button\"\n (click)=\"$event.stopPropagation(); deselect(filter)\"\n >\n <i c8yIcon=\"times\"></i>\n </button>\n <span\n class=\"circle-icon-wrapper circle-icon-wrapper--small\"\n [ngStyle]=\"{ 'background-color': filter.color }\"\n >\n <i\n class=\"stroked-icon\"\n c8yIcon=\"bell\"\n ></i>\n </span>\n <span\n class=\"text-truncate text-12 flex-grow\"\n [title]=\"filter.filters.type\"\n [attr.aria-label]=\"filter.filters.type\"\n >\n {{ filter.filters.type }}\n </span>\n </span>\n </ng-container>\n <ng-template #allTypes>\n <span class=\"text-nowrap\">\n {{ 'All alarm types' | translate }}\n </span>\n </ng-template>\n </div>\n <div class=\"input-group-btn input-group-btn--last text-center\">\n <button\n class=\"btn-default btn\"\n [title]=\"'Clear filters' | translate\"\n *ngIf=\"activeFilters.length\"\n (click)=\"deselectAll()\"\n >\n <i c8yIcon=\"times\"></i>\n </button>\n <button\n class=\"btn-default btn btn--caret\"\n [title]=\"'Alarm types' | translate\"\n data-cy=\"c8y-alarm-type-filter\"\n dropdownToggle\n >\n <i class=\"caret\"></i>\n </button>\n </div>\n </div>\n <div\n class=\"dropdown-menu dropdown-menu-action-bar\"\n style=\"min-width: 250px\"\n *dropdownMenu\n >\n <div class=\"p-16 bg-level-2\">\n <div>\n <p>\n <i\n class=\"text-info m-r-4\"\n [c8yIcon]=\"'info-circle'\"\n ></i>\n <strong tanslate>The list below may not be complete.</strong>\n </p>\n <p tanslate>\n Recent alarms are displayed below. Past alarms might not be shown. Optionally you can\n add a custom alarm.\n </p>\n </div>\n </div>\n <c8y-list-group>\n <div class=\"input-group p-t-16 p-b-16 p-r-32 p-l-32 separator-bottom\">\n <input\n class=\"form-control\"\n type=\"text\"\n [placeholder]=\"'Custom alarm type' | translate\"\n [(ngModel)]=\"customAlarmTypeInput\"\n (keydown)=\"confirmWithEnter($event)\"\n />\n <div class=\"input-group-btn\">\n <button\n class=\"btn-dot text-primary\"\n [attr.aria-label]=\"'Add custom alarm' | translate\"\n [tooltip]=\"'Add' | translate\"\n placement=\"top\"\n [delay]=\"500\"\n (click)=\"addCustomAlarmType()\"\n >\n <i c8yIcon=\"plus-circle\"></i>\n </button>\n </div>\n </div>\n\n <c8y-li\n class=\"c8y-list__item__collapse--container-small cdk-drag\"\n style=\"cursor: pointer\"\n *ngFor=\"let alarmType of possibleFilters\"\n (click)=\"toggleAlarmType(alarmType)\"\n >\n <c8y-li-checkbox\n class=\"a-s-center m-t-4 p-r-0 p-l-0\"\n [selected]=\"alarmType.__active\"\n (click)=\"$event.stopPropagation()\"\n (change)=\"toggleAlarmType(alarmType); $event.stopPropagation()\"\n ></c8y-li-checkbox>\n <div class=\"d-flex a-i-center p-l-4\">\n <div class=\"c8y-list__item__colorpicker p-t-0 p-b-0 p-l-0\">\n <div class=\"c8y-colorpicker c8y-colorpicker--alarm\">\n <span\n class=\"circle-icon-wrapper\"\n [ngStyle]=\"{ 'background-color': alarmType.color }\"\n >\n <i\n class=\"stroked-icon\"\n [c8yIcon]=\"'bell'\"\n ></i>\n </span>\n </div>\n </div>\n <span\n class=\"text-truncate text-12 flex-grow\"\n [title]=\"alarmType.label\"\n [attr.aria-label]=\"alarmType.label\"\n >\n {{ alarmType.label }}\n </span>\n <button\n class=\"btn-dot btn-dot--danger\"\n [attr.aria-label]=\"'Remove' | translate\"\n tooltip=\"'Remove' | translate\"\n placement=\"top\"\n *ngIf=\"alarmType.__target === null\"\n [delay]=\"500\"\n (click)=\"removeCustomAlarm(alarmType); $event.stopPropagation()\"\n >\n <i c8yIcon=\"minus-circle\"></i>\n </button>\n </div>\n </c8y-li>\n <c8y-li *ngIf=\"possibleFilters.length === 0\">\n <c8y-ui-empty-state\n class=\"p-t-8\"\n icon=\"c8y-alarm\"\n [title]=\"'No alarm found' | translate\"\n [subtitle]=\"\n 'There is no alarm to filter. You can still add a custom alarm.' | translate\n \"\n [horizontal]=\"true\"\n ></c8y-ui-empty-state>\n </c8y-li>\n <div class=\"sticky-bottom p-16\">\n <button\n class=\"btn btn-block btn-primary\"\n [disabled]=\"possibleFilters.length === 0\"\n (click)=\"applyFilterChange(); $event.stopPropagation(); filtersDropdown.hide()\"\n translate\n >\n Apply\n </button>\n </div>\n </c8y-list-group>\n </div>\n </div>\n</div>\n", dependencies: [{ kind: "directive", type: BsDropdownDirective, selector: "[bsDropdown], [dropdown]", inputs: ["placement", "triggers", "container", "dropup", "autoClose", "isAnimated", "insideClick", "isDisabled", "isOpen"], outputs: ["isOpenChange", "onShown", "onHidden"], exportAs: ["bs-dropdown"] }, { kind: "directive", type: CdkTrapFocus, selector: "[cdkTrapFocus]", inputs: ["cdkTrapFocus", "cdkTrapFocusAutoCapture"], exportAs: ["cdkTrapFocus"] }, { kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: NgFor, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "directive", type: NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: BsDropdownToggleDirective, selector: "[bsDropdownToggle],[dropdownToggle]", exportAs: ["bs-dropdown-toggle"] }, { kind: "directive", type: BsDropdownMenuDirective, selector: "[bsDropdownMenu],[dropdownMenu]", exportAs: ["bs-dropdown-menu"] }, { kind: "component", type: ListGroupComponent, selector: "c8y-list-group" }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: TooltipDirective, selector: "[tooltip], [tooltipHtml]", inputs: ["adaptivePosition", "tooltip", "placement", "triggers", "container", "containerClass", "boundariesElement", "isOpen", "isDisabled", "delay", "tooltipHtml", "tooltipPlacement", "tooltipIsOpen", "tooltipEnable", "tooltipAppendToBody", "tooltipAnimation", "tooltipClass", "tooltipContext", "tooltipPopupDelay", "tooltipFadeDuration", "tooltipTrigger"], outputs: ["tooltipChange", "onShown", "onHidden", "tooltipStateChanged"], exportAs: ["bs-tooltip"] }, { kind: "component", type: ListItemComponent, selector: "c8y-list-item, c8y-li", inputs: ["active", "highlighted", "emptyActions", "dense", "collapsed", "selectable"], outputs: ["collapsedChange"] }, { kind: "component", type: ListItemCheckboxComponent, selector: "c8y-list-item-checkbox, c8y-li-checkbox", inputs: ["selected", "indeterminate", "disabled", "displayAsSwitch"], outputs: ["onSelect"] }, { kind: "component", type: EmptyStateComponent, selector: "c8y-ui-empty-state", inputs: ["icon", "title", "subtitle", "horizontal"] }, { kind: "directive", type: C8yTranslateDirective, selector: "[translate],[ngx-translate]" }, { kind: "pipe", type: C8yTranslatePipe, name: "translate" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AlarmsTypeFilterComponent, decorators: [{
type: Component,
args: [{ selector: 'c8y-alarms-type-filter', imports: [
BsDropdownDirective,
CdkTrapFocus,
NgIf,
NgFor,
IconDirective,
NgStyle,
BsDropdownToggleDirective,
BsDropdownMenuDirective,
ListGroupComponent,
FormsModule,
TooltipDirective,
ListItemComponent,
ListItemCheckboxComponent,
EmptyStateComponent,
C8yTranslateDirective,
C8yTranslatePipe
], template: "<div class=\"d-flex a-i-center\">\n <div\n class=\"dropdown\"\n title=\"{{ 'Filter by alarm types' | translate }}\"\n dropdown\n #filtersDropdown=\"bs-dropdown\"\n [cdkTrapFocus]=\"filtersDropdown.isOpen\"\n (onHidden)=\"resetFilters()\"\n [insideClick]=\"true\"\n >\n <div class=\"input-group fit-w\">\n <div class=\"form-control d-flex a-i-center inner-scroll\">\n <ng-container *ngIf=\"activeFilters.length > 0; else allTypes\">\n <span\n class=\"tag tag--info chip\"\n style=\"max-width: 150px\"\n *ngFor=\"let filter of activeFilters\"\n >\n <button\n class=\"btn btn-xs btn-clean text-10\"\n title=\"{{ 'Remove' | translate }}\"\n type=\"button\"\n (click)=\"$event.stopPropagation(); deselect(filter)\"\n >\n <i c8yIcon=\"times\"></i>\n </button>\n <span\n class=\"circle-icon-wrapper circle-icon-wrapper--small\"\n [ngStyle]=\"{ 'background-color': filter.color }\"\n >\n <i\n class=\"stroked-icon\"\n c8yIcon=\"bell\"\n ></i>\n </span>\n <span\n class=\"text-truncate text-12 flex-grow\"\n [title]=\"filter.filters.type\"\n [attr.aria-label]=\"filter.filters.type\"\n >\n {{ filter.filters.type }}\n </span>\n </span>\n </ng-container>\n <ng-template #allTypes>\n <span class=\"text-nowrap\">\n {{ 'All alarm types' | translate }}\n </span>\n </ng-template>\n </div>\n <div class=\"input-group-btn input-group-btn--last text-center\">\n <button\n class=\"btn-default btn\"\n [title]=\"'Clear filters' | translate\"\n *ngIf=\"activeFilters.length\"\n (click)=\"deselectAll()\"\n >\n <i c8yIcon=\"times\"></i>\n </button>\n <button\n class=\"btn-default btn btn--caret\"\n [title]=\"'Alarm types' | translate\"\n data-cy=\"c8y-alarm-type-filter\"\n dropdownToggle\n >\n <i class=\"caret\"></i>\n </button>\n </div>\n </div>\n <div\n class=\"dropdown-menu dropdown-menu-action-bar\"\n style=\"min-width: 250px\"\n *dropdownMenu\n >\n <div class=\"p-16 bg-level-2\">\n <div>\n <p>\n <i\n class=\"text-info m-r-4\"\n [c8yIcon]=\"'info-circle'\"\n ></i>\n <strong tanslate>The list below may not be complete.</strong>\n </p>\n <p tanslate>\n Recent alarms are displayed below. Past alarms might not be shown. Optionally you can\n add a custom alarm.\n </p>\n </div>\n </div>\n <c8y-list-group>\n <div class=\"input-group p-t-16 p-b-16 p-r-32 p-l-32 separator-bottom\">\n <input\n class=\"form-control\"\n type=\"text\"\n [placeholder]=\"'Custom alarm type' | translate\"\n [(ngModel)]=\"customAlarmTypeInput\"\n (keydown)=\"confirmWithEnter($event)\"\n />\n <div class=\"input-group-btn\">\n <button\n class=\"btn-dot text-primary\"\n [attr.aria-label]=\"'Add custom alarm' | translate\"\n [tooltip]=\"'Add' | translate\"\n placement=\"top\"\n [delay]=\"500\"\n (click)=\"addCustomAlarmType()\"\n >\n <i c8yIcon=\"plus-circle\"></i>\n </button>\n </div>\n </div>\n\n <c8y-li\n class=\"c8y-list__item__collapse--container-small cdk-drag\"\n style=\"cursor: pointer\"\n *ngFor=\"let alarmType of possibleFilters\"\n (click)=\"toggleAlarmType(alarmType)\"\n >\n <c8y-li-checkbox\n class=\"a-s-center m-t-4 p-r-0 p-l-0\"\n [selected]=\"alarmType.__active\"\n (click)=\"$event.stopPropagation()\"\n (change)=\"toggleAlarmType(alarmType); $event.stopPropagation()\"\n ></c8y-li-checkbox>\n <div class=\"d-flex a-i-center p-l-4\">\n <div class=\"c8y-list__item__colorpicker p-t-0 p-b-0 p-l-0\">\n <div class=\"c8y-colorpicker c8y-colorpicker--alarm\">\n <span\n class=\"circle-icon-wrapper\"\n [ngStyle]=\"{ 'background-color': alarmType.color }\"\n >\n <i\n class=\"stroked-icon\"\n [c8yIcon]=\"'bell'\"\n ></i>\n </span>\n </div>\n </div>\n <span\n class=\"text-truncate text-12 flex-grow\"\n [title]=\"alarmType.label\"\n [attr.aria-label]=\"alarmType.label\"\n >\n {{ alarmType.label }}\n </span>\n <button\n class=\"btn-dot btn-dot--danger\"\n [attr.aria-label]=\"'Remove' | translate\"\n tooltip=\"'Remove' | translate\"\n placement=\"top\"\n *ngIf=\"alarmType.__target === null\"\n [delay]=\"500\"\n (click)=\"removeCustomAlarm(alarmType); $event.stopPropagation()\"\n >\n <i c8yIcon=\"minus-circle\"></i>\n </button>\n </div>\n </c8y-li>\n <c8y-li *ngIf=\"possibleFilters.length === 0\">\n <c8y-ui-empty-state\n class=\"p-t-8\"\n icon=\"c8y-alarm\"\n [title]=\"'No alarm found' | translate\"\n [subtitle]=\"\n 'There is no alarm to filter. You can still add a custom alarm.' | translate\n \"\n [horizontal]=\"true\"\n ></c8y-ui-empty-state>\n </c8y-li>\n <div class=\"sticky-bottom p-16\">\n <button\n class=\"btn btn-block btn-primary\"\n [disabled]=\"possibleFilters.length === 0\"\n (click)=\"applyFilterChange(); $event.stopPropagation(); filtersDropdown.hide()\"\n translate\n >\n Apply\n </button>\n </div>\n </c8y-list-group>\n </div>\n </div>\n</div>\n" }]
}], ctorParameters: () => [{ type: i1$3.AlarmEventSelectorService }, { type: i1.ActivatedRoute }, { type: i1.Router }, { type: i3.ColorService }], propDecorators: { alarms: [{
type: Input
}], possibleFilters: [{
type: Input
}], activeFilters: [{
type: Input
}], onFilterChanged: [{
type: Output
}] } });
class AlarmsComponent {
constructor(activatedRoute, alarmsViewService, alarmWithChildrenRealtimeService, alertService, contextRouteService, modalService, translateService, router, gainsightService, alarmsActivityTrackerService) {
this.activatedRoute = activatedRoute;
this.alarmsViewService = alarmsViewService;
this.alarmWithChildrenRealtimeService = alarmWithChildrenRealtimeService;
this.alertService = alertService;
this.contextRouteService = contextRouteService;
this.modalService = modalService;
this.translateService = translateService;
this.router = router;
this.gainsightService = gainsightService;
this.alarmsActivityTrackerService = alarmsActivityTrackerService;
this.NEW_REALTIME_ALARM_MESSAGE = this.alarmsViewService.REALTIME_UPDATE_ALARMS_MESSAGE;
this.TITLE = gettext('Alarms');
this.REFRESH_LABEL = gettext('Refresh');
this.alarms$ = new BehaviorSubject(null);
this.isLoading$ = new BehaviorSubject(false);
this.isRealtimeActive = new BehaviorSubject(false);
this.shouldShowIntervalToggle$ = new BehaviorSubject(true);
this.isRealtimeToggleOn = true;
this.typeFilters = [];
this.isDisabled = false;
this.isListScrolled = signal(false);
this.destroy$ = new Subject();
this.selectedSeverities = Object.keys(SEVERITY_LABELS);
this.severityOptions = DEFAULT_SEVERITY_VALUES;
this.showCleared = false;
this.WAIT_TIME_AVOID_MULTIPLE_REQUEST_BY_PARAM_CHANGE = 100;
this.isIntervalRefresh = this.alarmsViewService.isIntervalRefresh();
this.initializeContextSourceId();
}
ngOnInit() {
this.alarmsActivityTrackerService.setupEventListenersForGainsight();
this.alarmsActivityTrackerService.resetInactivityTimer();
this.alarmsActivityTrackerService.isUserActive$
.pipe(distinctUntilChanged(), takeUntil(this.destroy$))
.subscribe(isActive => isActive
? this.alarmsActivityTrackerService.setGainsightInterval()
: this.alarmsActivityTrackerService.clearGainsightInterval());
const isInDetailView = !!this.activatedRoute.children[0]?.snapshot.params.id;
if (isInDetailView) {
this.changeInterval(false);
}
if (!this.isIntervalRefresh) {
this.handleLegacyRealtime();
}
this.alarmsViewService.reloadAlarmsList$
.pipe(debounceTime(this.WAIT_TIME_AVOID_MULTIPLE_REQUEST_BY_PARAM_CHANGE), takeUntil(this.destroy$))
.subscribe((value) => {
this.updateAlarms(value);
});
if (this.isIntervalRefresh) {
this.alarmsViewService.isIntervalEnabled$
.pipe(takeUntil(this.destroy$), filter(() => !this.isListScrolled()))
.subscribe(value => this.changeInterval(value));
}
this.alarmsViewService.closeDetailsView$.subscribe(async () => await this.alarmsViewService.closeDetailsView(this.activatedRoute));
}
ngOnDestroy() {
this.gainsightService.triggerEvent(PRODUCT_EXPERIENCE_ALARMS.EVENTS.ALARMS, {
component: PRODUCT_EXPERIENCE_ALARMS.COMPONENTS.ALARMS,
action: PRODUCT_EXPERIENCE_ALARMS.ACTIONS.USER_SPEND_TIME_ON_COMPONENT,
userSpendTime: this.alarmsViewService.convertSecondsToTime(this.alarmsActivityTrackerService.userSecondsSpendOnPage)
});
this.alarmsActivityTrackerService.clearGainsightInterval();
this.destroy$.next();
this.destroy$.complete();
}
visibilityChange() {
if (document.hidden) {
this.alarmsActivityTrackerService.clearGainsightInterval();
return;
}
this.alarmsActivityTrackerService.setGainsightInterval();
}
applyTypeFilters(typeFilters) {
this.typeFilters = typeFilters;
setTimeout(() => this.alarmsViewService.updateAlarmList(typeFilters.length > 0 ? 'gainsightTypeFilters' : null));
this.isRealtimeActive.next(false);
}
applyFormFilters({ severityOptions, showCleared, selectedDates }) {
this.severityOptions = severityOptions;
this.showCleared = showCleared;
this.selectedDates = selectedDates;
this.selectedSeverities = this.alarmsViewService.updateSelectedSeverities(this.severityOptions);
this.alarmsViewService.updateAlarmList();
this.isRealtimeActive.next(false);
}
async applyDateFilter(selectedDates) {
this.alarms$.next(await this.alarmsViewService.retrieveAlarmsByDate(selectedDates));
this.isRealtimeActive.next(false);
}
async clearAll() {
try {
const translatedBody = this.translateService.instant(gettext('Do you really want to clear all alarms of selected severities?'));
await this.modalService.confirm(gettext('Confirm clearing alarms?'), translatedBody, 'danger', {
ok: gettext('Confirm'),
cancel: gettext('Cancel')
});
}
catch {
// modal canceled
return;
}
await this.clearAlarms();
this.alarmsViewService.closeDetailsView$.next();
}
refresh() {
this.updateAlarms(null);
this.isRealtimeActive.next(false);
}
changeInterval(value = true) {
this.shouldShowIntervalToggle$.next(value);
}
toggleRealtimeState() {
this.isRealtimeToggleOn = !this.isRealtimeToggleOn;
}
handleLegacyRealtime() {
this.realtimeIconTitle = this.translateService.instant(gettext('Realtime active'));
this.subscribeToRealtimeUpdates();
}
async clearAlarms() {
try {
const result = await this.alarmsViewService.clearAllActiveAlarms(this.selectedSeverities, this.contextSourceId);
if (result.resolvedImmediately) {
this.alertService.success(this.translateService.instant(gettext('Alarms cleared.')));
this.refresh();
}
else {
this.alertService.success(this.translateService.instant(gettext('Alarms are being cleared in background.')));
}
}
catch (error) {
this.alertService.addServerFailure(error);
}
}
async getAlarms() {
try {
this.isLoading$.next(true);
const additionalFilter = {};
if (this.contextSourceId) {
(additionalFilter.source = this.contextSourceId),
(additionalFilter.withSourceAssets = true),
(additionalFilter.withSourceDevices = true);
}
if (this.typeFilters.length > 0) {
additionalFilter.type = this.typeFilters.map(({ filters }) => filters.type).join(',');
}
return await this.alarmsViewService.retrieveFilteredAlarms(this.selectedSeverities, this.showCleared, this.selectedDates, additionalFilter);
}
catch (error) {
if (error?.res?.status === 403) {
this.isDisabled = true;
return;
}
this.alertService.addServerFailure(error);
}
finally {
this.isLoading$.next(false);
}
}
async updateAlarms(value) {
const alarms = await this.getAlarms();
this.alarms$.next(alarms);
if (value === 'gainsightTypeFilters') {
this.gainsightService.triggerEvent(PRODUCT_EXPERIENCE_ALARMS.EVENTS.ALARMS, {
component: PRODUCT_EXPERIENCE_ALARMS.COMPONENTS.ALARMS_TYPE_FILTER,
action: PRODUCT_EXPERIENCE_ALARMS.ACTIONS.APPLY_TYPE_FILTER,
alarmsCount: alarms.data.length
});
}
}
subscribeToRealtimeUpdates() {
this.alarmWithChildrenRealtimeService
.onAll$(this.contextSourceId)
.pipe(takeUntil(this.destroy$), throttleTime(THROTTLE_REALTIME_REFRESH, undefined, { trailing: true }))
.subscribe(() => {
if (this.isRealtimeToggleOn) {
this.refresh();
}
else {
this.isRealtimeActive.next(true);
}
});
}
initializeContextSourceId() {
const routeContext = this.contextRouteService.getContextData(this.activatedRoute);
if (!routeContext) {
return;
}
const { context, contextData } = routeContext;
if ([ViewContext.Device, ViewContext.Group, ViewContext.Service, ViewContext.Simulators].includes(context)) {
this.contextSourceId = contextData?.id;
}
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AlarmsComponent, deps: [{ token: i1.ActivatedRoute }, { token: AlarmsViewService }, { token: i3.AlarmWithChildrenRealtimeService }, { token: i3.AlertService }, { token: i3.ContextRouteService }, { token: i3.ModalService }, { token: i1$1.TranslateService }, { token: i1.Router }, { token: i3.GainsightService }, { token: AlarmsActivityTrackerService }], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.14", type: AlarmsComponent, isStandalone: true, selector: "c8y-alarms", host: { listeners: { "document:visibilitychange": "visibilityChange()" } }, providers: [AlarmWithChildrenRealtimeService, AlarmsActivityTrackerService], viewQueries: [{ propertyName: "alarmIntervalRefreshComponent", first: true, predicate: AlarmsIntervalRefreshComponent, descendants: true }], ngImport: i0, template: "<ng-container *ngIf=\"(activatedRoute.data | async)?.title\">\n <c8y-title>{{ TITLE | translate }}</c8y-title>\n</ng-container>\n\n<c8y-action-bar-item\n [placement]=\"'left'\"\n itemClass=\"navbar-form min-width-fit\"\n>\n <c8y-alarms-filter\n class=\"d-block fit-w\"\n [contextSourceId]=\"contextSourceId\"\n (onFilterApplied)=\"applyFormFilters($event)\"\n ></c8y-alarms-filter>\n</c8y-action-bar-item>\n\n<c8y-action-bar-item\n [placement]=\"'left'\"\n itemClass=\"navbar-form min-width-fit\"\n>\n <c8y-alarms-date-filter (dateFilterChange)=\"applyFormFilters($event)\"></c8y-alarms-date-filter>\n</c8y-action-bar-item>\n\n<c8y-action-bar-item\n [placement]=\"'left'\"\n itemClass=\"navbar-form\"\n>\n <c8y-alarms-type-filter\n class=\"d-block fit-w\"\n [alarms]=\"alarms$ | async\"\n (onFilterChanged)=\"applyTypeFilters($event)\"\n ></c8y-alarms-type-filter>\n</c8y-action-bar-item>\n\n<c8y-action-bar-item\n [placement]=\"'right'\"\n [priority]=\"0\"\n>\n <button\n class=\"btn btn-link\"\n [title]=\"'Clear all alarms' | translate\"\n type=\"button\"\n (click)=\"clearAll()\"\n data-cy=\"c8y-alarms-view--clear-all-button\"\n >\n <i c8yIcon=\"c8y-alert-idle\"></i>\n {{ 'Clear all`alarms`' | translate }}\n </button>\n</c8y-action-bar-item>\n\n<!--Realtime button-->\n<ng-template #realtimeRefresh>\n <div class=\"input-group-btn\">\n <button\n class=\"btn btn-default btn-sm\"\n [attr.aria-label]=\"'Refresh' | translate\"\n [tooltip]=\"\n (isRealtimeActive | async)\n ? (NEW_REALTIME_ALARM_MESSAGE | translate)\n : (REFRESH_LABEL | translate)\n \"\n placement=\"left\"\n container=\"body\"\n type=\"button\"\n [adaptivePosition]=\"false\"\n [delay]=\"500\"\n [disabled]=\"isLoading$ | async\"\n (click)=\"refresh()\"\n >\n <span\n class=\"tag tag--info m-r-8\"\n *ngIf=\"isRealtimeActive | async\"\n >\n {{ 'New alarms' | translate }}\n </span>\n <i\n c8yIcon=\"refresh\"\n [ngClass]=\"{ 'icon-spin': isLoading$ | async }\"\n ></i>\n </button>\n <button\n class=\"c8y-realtime btn btn-default btn-sm\"\n [attr.aria-label]=\"realtimeIconTitle\"\n [tooltip]=\"realtimeIconTitle\"\n placement=\"bottom\"\n type=\"button\"\n data-cy=\"c8y-alarms--realtime-button\"\n [container]=\"'body'\"\n (click)=\"toggleRealtimeState()\"\n >\n <span\n class=\"c8y-pulse m-0\"\n [ngClass]=\"{\n active: isRealtimeToggleOn,\n inactive: !isRealtimeToggleOn\n }\"\n ></span>\n </button>\n </div>\n</ng-template>\n\n<c8y-help\n src=\"/docs/device-management-application/monitoring-and-controlling-devices/#working-with-alarms\"\n></c8y-help>\n\n<div class=\"card content-fullpage split-view--5-7 grid__row--1\">\n <c8y-alarms-list\n class=\"d-contents\"\n [isInitialLoading]=\"isLoading$ | async\"\n [alarms]=\"alarms$ | async\"\n [typeFilters]=\"typeFilters\"\n (onScrollingStateChange)=\"changeInterval(!$event); isListScrolled.set($event)\"\n (onSelectedAlarm)=\"changeInterval(false)\"\n [splitView]=\"true\"\n [hasPermissions]=\"!isDisabled\"\n >\n <ng-container *ngIf=\"isIntervalRefresh; else realtimeRefresh\">\n <c8y-alarms-interval-refresh\n [alarmsListLoading$]=\"isLoading$\"\n [isIntervalToggleEnabled]=\"shouldShowIntervalToggle$ | async\"\n (onCountdownEnded)=\"refresh()\"\n [isDisabled]=\"isDisabled\"\n ></c8y-alarms-interval-refresh>\n </ng-container>\n </c8y-alarms-list>\n\n <router-outlet class=\"d-contents\"></router-outlet>\n</div>\n", dependencies: [{ kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: TitleComponent, selector: "c8y-title", inputs: ["pageTitleUpdate"] }, { kind: "component", type: ActionBarItemComponent, selector: "c8y-action-bar-item", inputs: ["placement", "priority", "itemClass", "injector", "groupId", "inGroupPriority"] }, { kind: "component", type: AlarmsFilterComponent, selector: "c8y-alarms-filter", inputs: ["contextSourceId"], outputs: ["onFilterApplied"] }, { kind: "component", type: AlarmsDateFilterComponent, selector: "c8y-alarms-date-filter", inputs: ["DEFAULT_INTERVAL", "updateQueryParams", "date"], outputs: ["dateFilterChange"] }, { kind: "component", type: AlarmsTypeFilterComponent, selector: "c8y-alarms-type-filter", inputs: ["alarms", "possibleFilters", "activeFilters"], outputs: ["onFilterChanged"] }, { kind: "directive", type: IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "directive", type: TooltipDirective, selector: "[tooltip], [tooltipHtml]", inputs: ["adaptivePosition", "tooltip", "placement", "triggers", "container", "containerClass", "boundariesElement", "isOpen", "isDisabled", "delay", "tooltipHtml", "tooltipPlacement", "tooltipIsOpen", "tooltipEnable", "tooltipAppendToBody", "tooltipAnimation", "tooltipClass", "tooltipContext", "tooltipPopupDelay", "tooltipFadeDuration", "tooltipTrigger"], outputs: ["tooltipChange", "onShown", "onHidden", "tooltipStateChanged"], exportAs: ["bs-tooltip"] }, { kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "component", type: HelpComponent, selector: "c8y-help", inputs: ["src", "isCollapsed", "priority", "icon"] }, { kind: "component", type: AlarmsListComponent, selector: "c8y-alarms-list", inputs: ["alarms", "hasPermissions", "typeFilters", "loadMoreMode", "navigationOptions", "isInitialLoading", "splitView"], outputs: ["onSelectedAlarm", "onScrollingStateChange"] }, { kind: "component", type: AlarmsIntervalRefreshComponent, selector: "c8y-alarms-interval-refresh", inputs: ["isDisabled", "alarmsListLoading$", "isIntervalToggleEnabled"], outputs: ["onCountdownEnded"] }, { kind: "directive", type: RouterOutlet, selector: "router-outlet", inputs: ["name", "routerOutletData"], outputs: ["activate", "deactivate", "attach", "detach"], exportAs: ["outlet"] }, { kind: "pipe", type: C8yTranslatePipe, name: "translate" }, { kind: "pipe", type: AsyncPipe, name: "async" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AlarmsComponent, decorators: [{
type: Component,
args: [{ selector: 'c8y-alarms', providers: [AlarmWithChildrenRealtimeService, AlarmsActivityTrackerService], imports: [
NgIf,
TitleComponent,
ActionBarItemComponent,
AlarmsFilterComponent,
AlarmsDateFilterComponent,
AlarmsTypeFilterComponent,
IconDirective,
TooltipDirective,
NgClass,
HelpComponent,
AlarmsListComponent,
AlarmsIntervalRefreshComponent,
RouterOutlet,
C8yTranslatePipe,
AsyncPipe
], template: "<ng-container *ngIf=\"(activatedRoute.data | async)?.title\">\n <c8y-title>{{ TITLE | translate }}</c8y-title>\n</ng-container>\n\n<c8y-action-bar-item\n [placement]=\"'left'\"\n itemClass=\"navbar-form min-width-fit\"\n>\n <c8y-alarms-filter\n class=\"d-block fit-w\"\n [contextSourceId]=\"contextSourceId\"\n (onFilterApplied)=\"applyFormFilters($event)\"\n ></c8y-alarms-filter>\n</c8y-action-bar-item>\n\n<c8y-action-bar-item\n [placement]=\"'left'\"\n itemClass=\"navbar-form min-width-fit\"\n>\n <c8y-alarms-date-filter (dateFilterChange)=\"applyFormFilters($event)\"></c8y-alarms-date-filter>\n</c8y-action-bar-item>\n\n<c8y-action-bar-item\n [placement]=\"'left'\"\n itemClass=\"navbar-form\"\n>\n <c8y-alarms-type-filter\n class=\"d-block fit-w\"\n [alarms]=\"alarms$ | async\"\n (onFilterChanged)=\"applyTypeFilters($event)\"\n ></c8y-alarms-type-filter>\n</c8y-action-bar-item>\n\n<c8y-action-bar-item\n [placement]=\"'right'\"\n [priority]=\"0\"\n>\n <button\n class=\"btn btn-link\"\n [title]=\"'Clear all alarms' | translate\"\n type=\"button\"\n (click)=\"clearAll()\"\n data-cy=\"c8y-alarms-view--clear-all-button\"\n >\n <i c8yIcon=\"c8y-alert-idle\"></i>\n {{ 'Clear all`alarms`' | translate }}\n </button>\n</c8y-action-bar-item>\n\n<!--Realtime button-->\n<ng-template #realtimeRefresh>\n <div class=\"input-group-btn\">\n <button\n class=\"btn btn-default btn-sm\"\n [attr.aria-label]=\"'Refresh' | translate\"\n [tooltip]=\"\n (isRealtimeActive | async)\n ? (NEW_REALTIME_ALARM_MESSAGE | translate)\n : (REFRESH_LABEL | translate)\n \"\n placement=\"left\"\n container=\"body\"\n type=\"button\"\n [adaptivePosition]=\"false\"\n [delay]=\"500\"\n [disabled]=\"isLoading$ | async\"\n (click)=\"refresh()\"\n >\n <span\n class=\"tag tag--info m-r-8\"\n *ngIf=\"isRealtimeActive | async\"\n >\n {{ 'New alarms' | translate }}\n </span>\n <i\n c8yIcon=\"refresh\"\n [ngClass]=\"{ 'icon-spin': isLoading$ | async }\"\n ></i>\n </button>\n <button\n class=\"c8y-realtime btn btn-default btn-sm\"\n [attr.aria-label]=\"realtimeIconTitle\"\n [tooltip]=\"realtimeIconTitle\"\n placement=\"bottom\"\n type=\"button\"\n data-cy=\"c8y-alarms--realtime-button\"\n [container]=\"'body'\"\n (click)=\"toggleRealtimeState()\"\n >\n <span\n class=\"c8y-pulse m-0\"\n [ngClass]=\"{\n active: isRealtimeToggleOn,\n inactive: !isRealtimeToggleOn\n }\"\n ></span>\n </button>\n </div>\n</ng-template>\n\n<c8y-help\n src=\"/docs/device-management-application/monitoring-and-controlling-devices/#working-with-alarms\"\n></c8y-help>\n\n<div class=\"card content-fullpage split-view--5-7 grid__row--1\">\n <c8y-alarms-list\n class=\"d-contents\"\n [isInitialLoading]=\"isLoading$ | async\"\n [alarms]=\"alarms$ | async\"\n [typeFilters]=\"typeFilters\"\n (onScrollingStateChange)=\"changeInterval(!$event); isListScrolled.set($event)\"\n (onSelectedAlarm)=\"changeInterval(false)\"\n [splitView]=\"true\"\n [hasPermissions]=\"!isDisabled\"\n >\n <ng-container *ngIf=\"isIntervalRefresh; else realtimeRefresh\">\n <c8y-alarms-interval-refresh\n [alarmsListLoading$]=\"isLoading$\"\n [isIntervalToggleEnabled]=\"shouldShowIntervalToggle$ | async\"\n (onCountdownEnded)=\"refresh()\"\n [isDisabled]=\"isDisabled\"\n ></c8y-alarms-interval-refresh>\n </ng-container>\n </c8y-alarms-list>\n\n <router-outlet class=\"d-contents\"></router-outlet>\n</div>\n" }]
}], ctorParameters: () => [{ type: i1.ActivatedRoute }, { type: AlarmsViewService }, { type: i3.AlarmWithChildrenRealtimeService }, { type: i3.AlertService }, { type: i3.ContextRouteService }, { type: i3.ModalService }, { type: i1$1.TranslateService }, { type: i1.Router }, { type: i3.GainsightService }, { type: AlarmsActivityTrackerService }], propDecorators: { alarmIntervalRefreshComponent: [{
type: ViewChild,
args: [AlarmsIntervalRefreshComponent]
}], visibilityChange: [{
type: HostListener,
args: ['document:visibilitychange']
}] } });
function getViewContextRoutes(contexts) {
return contexts.map(context => ({
context,
path: 'details',
icon: 'bell',
label: gettext('Details'),
component: AlarmInfoComponent,
tabsOutlet: 'alarms'
}));
}
function getChildrenForViewContext(context) {
return [
{
path: '',
component: AlarmEmptyComponent
},
{
path: ':id',
rootContext: context,
component: ContextRouteComponent,
canActivate: [ContextRouteGuard],
data: { context, contextData: {} },
resolve: {
tabs: RouterTabsResolver
}
}
];
}
const defaultAlarmsConfig = {
hybrid: true
};
class AlarmsModule {
static config(config = {}) {
const alarmsConfig = { ...defaultAlarmsConfig, ...config };
return {
ngModule: AlarmsModule,
providers: [
...(alarmsConfig.hybrid ? [smartRulesUpgradeServiceProvider] : []),
hookNavigator(alarmsConfig.rootNavigatorNode),
hookRoute(alarmsConfig.route),
{
provide: ALARMS_MODULE_CONFIG,
useValue: alarmsConfig
}
]
};
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AlarmsModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.14", ngImport: i0, type: AlarmsModule, imports: [A11yModule,
BsDropdownModule,
CommonModule,
CoreModule,
HeaderModule,
PopoverModule, i1$4.TooltipModule, C8yTranslateModule,
RouterModule,
AlarmEventSelectorModule,
RouterModule,
AlarmListIndicatorPipe,
AlarmDetailsButtonPipe,
AlarmSeverityToIconPipe,
DynamicComponentModule,
IntervalPickerComponent,
AlarmDetailsComponent,
AlarmInfoComponent,
AlarmsComponent,
AlarmSeveritiesToTitlePipe,
AlarmsFilterComponent,
AlarmsIconComponent,
AlarmsIntervalRefreshComponent,
AlarmsListComponent,
AlarmStatusToIconPipe,
AuditChangesMessagePipe,
AlarmSeverityToLabelPipe,
AlarmStatusToLabelPipe,
AlarmEmptyComponent,
AlarmsDateFilterComponent,
AlarmsTypeFilterComponent], exports: [AlarmsComponent, AlarmsListComponent, AlarmsFilterComponent, AlarmsDateFilterComponent] }); }
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AlarmsModule, providers: [
TitleCasePipe,
RelativeTimePipe,
{
provide: ALARMS_MODULE_CONFIG,
useValue: defaultAlarmsConfig
}
], imports: [A11yModule,
BsDropdownModule,
CommonModule,
CoreModule,
HeaderModule,
PopoverModule,
TooltipModule.forRoot(),
C8yTranslateModule,
RouterModule,
AlarmEventSelectorModule,
RouterModule,
DynamicComponentModule,
IntervalPickerComponent,
AlarmDetailsComponent,
AlarmInfoComponent,
AlarmsComponent,
AlarmsFilterComponent,
AlarmsIntervalRefreshComponent,
AlarmsListComponent,
AlarmEmptyComponent,
AlarmsDateFilterComponent,
AlarmsTypeFilterComponent] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AlarmsModule, decorators: [{
type: NgModule,
args: [{
exports: [AlarmsComponent, AlarmsListComponent, AlarmsFilterComponent, AlarmsDateFilterComponent],
providers: [
TitleCasePipe,
RelativeTimePipe,
{
provide: ALARMS_MODULE_CONFIG,
useValue: defaultAlarmsConfig
}
],
imports: [
A11yModule,
BsDropdownModule,
CommonModule,
CoreModule,
HeaderModule,
PopoverModule,
TooltipModule.forRoot(),
C8yTranslateModule,
RouterModule,
AlarmEventSelectorModule,
RouterModule,
AlarmListIndicatorPipe,
AlarmDetailsButtonPipe,
AlarmSeverityToIconPipe,
DynamicComponentModule,
IntervalPickerComponent,
AlarmDetailsComponent,
AlarmInfoComponent,
AlarmsComponent,
AlarmSeveritiesToTitlePipe,
AlarmsFilterComponent,
AlarmsIconComponent,
AlarmsIntervalRefreshComponent,
AlarmsListComponent,
AlarmStatusToIconPipe,
AuditChangesMessagePipe,
AlarmSeverityToLabelPipe,
AlarmStatusToLabelPipe,
AlarmEmptyComponent,
AlarmsDateFilterComponent,
AlarmsTypeFilterComponent
]
}]
}] });
/**
* Generated bundle index. Do not edit.
*/
export { ALARMS_MODULE_CONFIG, ALARMS_PATH, ALARM_DEFAULT_PROPERTIES, ALARM_SEVERITY_ICON, ALARM_SEVERITY_ICON_MAP, ALARM_STATUS_ICON, AlarmDetailsButtonPipe, AlarmDetailsButtonService, AlarmDetailsComponent, AlarmDetailsService, AlarmEmptyComponent, AlarmIconMap, AlarmInfoComponent, AlarmListIndicatorPipe, AlarmListIndicatorService, AlarmSeveritiesToTitlePipe, AlarmSeverityToIconPipe, AlarmSeverityToLabelPipe, AlarmStatusToIconPipe, AlarmStatusToLabelPipe, AlarmsActivityTrackerService, AlarmsComponent, AlarmsDateFilterComponent, AlarmsFilterComponent, AlarmsIconComponent, AlarmsIntervalRefreshComponent, AlarmsListComponent, AlarmsModule, AlarmsTypeFilterComponent, AlarmsViewService, AuditChangesMessagePipe, DEFAULT_ALARM_COUNTS, DEFAULT_SEVERITY_VALUES, DEFAULT_STATUS_VALUES, HELP_ICON, INTERVALS_EXTENDED, INTERVAL_TITLES_EXTENDED, Ng1SmartRulesUpgradeService, PRODUCT_EXPERIENCE_ALARMS, SmartRulesUpgradeServiceFactory, THROTTLE_REALTIME_REFRESH, getChildrenForViewContext, getViewContextRoutes, smartRulesUpgradeServiceProvider };
//# sourceMappingURL=c8y-ngx-components-alarms.mjs.map