@c8y/ngx-components
Version:
Angular modules for Cumulocity IoT applications
1,250 lines • 104 kB
JavaScript
import * as i0 from '@angular/core';
import { Injectable, Pipe, ViewChild, Input, Component, EventEmitter, Output, NgModule } from '@angular/core';
import * as i5 from '@angular/forms';
import { FormControl, NgForm, ControlContainer, FormsModule, ReactiveFormsModule } from '@angular/forms';
import * as i2 from '@c8y/client';
import { AlarmStatus, Severity, SEVERITY_LABELS, ALARM_STATUS_LABELS } from '@c8y/client';
import * as i2$1 from '@c8y/ngx-components';
import { gettext, C8yTranslateDirective, FormGroupComponent, RequiredInputPlaceholderDirective, IconDirective, MessagesComponent, MessageDirective, C8yTranslatePipe, IntervalBasedReload, WIDGET_TYPE_VALUES, CountdownIntervalComponent, DismissAlertStrategy, DynamicComponentAlert, globalAutoRefreshLoading, AlarmRealtimeService, AlarmWithChildrenRealtimeService, CommonModule, CountdownIntervalModule, DocsModule, FormsModule as FormsModule$1 } from '@c8y/ngx-components';
import * as i1 from '@c8y/ngx-components/alarms';
import { ALARM_STATUS_ICON, DEFAULT_SEVERITY_VALUES, DEFAULT_STATUS_VALUES, AlarmsDateFilterComponent, AlarmsListComponent, AlarmsModule } from '@c8y/ngx-components/alarms';
import * as i6 from '@ngx-translate/core';
import { omit, isEqual, isEmpty } from 'lodash-es';
import { BehaviorSubject, Subject } from 'rxjs';
import { startWith, pairwise, debounceTime, tap, takeUntil, skip } from 'rxjs/operators';
import * as i7 from '@c8y/ngx-components/context-dashboard';
import { PopoverDirective, PopoverModule } from 'ngx-bootstrap/popover';
import { NgIf, NgFor, NgClass, AsyncPipe, CommonModule as CommonModule$1 } from '@angular/common';
import * as i1$1 from 'ngx-bootstrap/tooltip';
import { TooltipDirective, TooltipModule } from 'ngx-bootstrap/tooltip';
import { RouterModule } from '@angular/router';
const ALARM_ORDER_VALUES = {
BY_ACTIVE: 'BY_ACTIVE',
BY_DATE_ASCENDING: 'BY_DATE_ASCENDING',
BY_DATE_DESCENDING: 'BY_DATE_DESCENDING',
BY_SEVERITY: 'BY_SEVERITY'
};
const ALARM_ORDER_LABELS = {
BY_ACTIVE: gettext('By active status'),
BY_DATE_ASCENDING: gettext('By date (ascending)'),
BY_DATE_DESCENDING: gettext('By date (descending)'),
BY_SEVERITY: gettext('By severity')
};
const ASSET_ALARMS_WIDGET_ID = 'Asset Alarms';
const RECENT_ALARMS_WIDGET_ID = 'Recent Alarms';
const GLOBAL_INTERVAL_OPTION = 'global-interval';
var DATE_SELECTION;
(function (DATE_SELECTION) {
DATE_SELECTION["CONFIG"] = "config";
DATE_SELECTION["VIEW_AND_CONFIG"] = "view_and_config";
DATE_SELECTION["DASHBOARD_CONTEXT"] = "dashboard_context";
})(DATE_SELECTION || (DATE_SELECTION = {}));
const DEFAULT_PAGE_SIZE = 20;
class AlarmWidgetService {
constructor(alarmsViewService) {
this.alarmsViewService = alarmsViewService;
}
/**
* Checks if the provided data follows the LegacyAlarmConfig structure.
*
* This function determines if a given data object is an instance of LegacyAlarmConfig
* by checking for the presence of the 'options' property.
*
* @param data - The data object to be checked.
* @returns - Returns `true` if the data object is a LegacyAlarmConfig, otherwise `false`.
*/
isOldAlarmConfigStructure(data) {
return data !== null && typeof data === 'object' && 'options' in data;
}
/**
* Creates predefined widget configuration object.
*
* This method creates a new configuration object based on
* a widgets ID (that determines if is a legacy Recent or Critical alarms widget).
*
* @param isIntervalRefresh - determines a type of a refresh.
* @param widgetId - determines if a config should be done for Recent or Critical alarms widget.
* @returns The new, predefined configuration object.
*/
getPredefinedConfiguration(isIntervalRefresh, widgetId) {
return {
order: ALARM_ORDER_VALUES.BY_ACTIVE,
severities: widgetId === RECENT_ALARMS_WIDGET_ID
? {
[Severity.CRITICAL]: true,
[Severity.MAJOR]: true,
[Severity.MINOR]: true,
[Severity.WARNING]: true
}
: {
[Severity.CRITICAL]: true,
[Severity.MAJOR]: false,
[Severity.MINOR]: false,
[Severity.WARNING]: false
},
status: widgetId === RECENT_ALARMS_WIDGET_ID
? {
[AlarmStatus.ACKNOWLEDGED]: true,
[AlarmStatus.ACTIVE]: true,
[AlarmStatus.CLEARED]: true
}
: {
[AlarmStatus.ACKNOWLEDGED]: false,
[AlarmStatus.ACTIVE]: true,
[AlarmStatus.CLEARED]: false
},
types: [''],
isRealtime: !isIntervalRefresh,
isAutoRefreshEnabled: true,
refreshInterval: isIntervalRefresh ? this.alarmsViewService.DEFAULT_INTERVAL_VALUE : undefined
};
}
/**
* Transforms a LegacyAlarmConfig object into an AlarmListWidgetConfig object.
*
* This function maps the properties from an old configuration structure (LegacyAlarmConfig)
* to a new configuration structure (AlarmListWidgetConfig).
*
* @param oldConfig - The old configuration object to be transformed.
* @returns - The new configuration object mapped from the old one.
*/
mapToNewConfigStructure(oldConfig, isIntervalRefresh) {
const order = oldConfig.options.orderMode === 'ACTIVE_FIRST'
? ALARM_ORDER_VALUES.BY_ACTIVE
: ALARM_ORDER_VALUES.BY_SEVERITY;
if (!this.isContainingAllSeverityTypes(oldConfig.options.severity)) {
oldConfig.options.severity = this.addAllMissingSeverityTypes(oldConfig.options.severity);
}
return {
order: order,
isRealtime: oldConfig.realtime,
device: oldConfig.device,
showAlarmsForChildren: true,
severities: oldConfig.options.severity,
status: this.allValuesFalse(oldConfig.options.status)
? {
[AlarmStatus.ACKNOWLEDGED]: true,
[AlarmStatus.ACTIVE]: true,
[AlarmStatus.CLEARED]: true
}
: oldConfig.options.status,
types: oldConfig.options.types?.length ? oldConfig.options.types : [''],
isAutoRefreshEnabled: true,
refreshInterval: isIntervalRefresh ? this.alarmsViewService.DEFAULT_INTERVAL_VALUE : undefined
};
}
/**
* Checks if the provided severity object contains all the predefined severity types.
*
* @param severity - A record object where keys are severity type strings and values are boolean.
* - This object is checked against the predefined severity types.
* @returns `true` if all predefined severity types are present in the severity object; otherwise, `false`.
*/
isContainingAllSeverityTypes(severity) {
return Object.keys(SEVERITY_LABELS).every(severityType => severityType in severity);
}
/**
* Adds any missing severity types to the provided severity object with a default value of `false`.
*
* @param severity - A record object where keys are severity type strings and values are boolean.
* - Missing severity types will be added to this object.
* @returns The modified severity object, which includes all predefined severity types, adding any
* that were missing with a value of `false`.
*/
addAllMissingSeverityTypes(severity) {
Object.keys(SEVERITY_LABELS).forEach(severityType => {
if (!(severityType in severity)) {
severity[severityType] = false;
}
});
return severity;
}
/**
* Maps an AlarmListWidgetConfig object to an AlarmQueryFilter.
*
* This function converts the provided AlarmListWidgetConfig into a format suitable for querying alarms.
*
* @param config - The configuration object for the alarm list widget.
* @param pageSize - Optional number specifying the size of the pages to be returned in the query.
* @returns - The query filter object constructed from the provided configuration.
*/
mapConfigToQueryFilter(config, pageSize) {
const filter = {
pageSize: pageSize || DEFAULT_PAGE_SIZE,
query: this.getOrderParameters(config.order),
severity: this.extractFilterParams(config.severities || {}),
status: this.extractFilterParams(config.status || {}),
type: (config.types || []).join(','),
withTotalPages: true
};
if (config.dateFilter) {
filter.lastUpdatedFrom =
typeof config.dateFilter[0] === 'string'
? config.dateFilter[0]
: config.dateFilter[0].toISOString();
filter.createdTo =
typeof config.dateFilter[1] === 'string'
? config.dateFilter[1]
: config.dateFilter[1].toISOString();
}
if (config.device) {
filter.source = config.device.id;
filter.withSourceAssets = true;
filter.withSourceDevices = true;
filter.withSourceAssets = config.showAlarmsForChildren ?? true;
filter.withSourceDevices = config.showAlarmsForChildren ?? true;
}
return filter;
}
/**
* Extracts and concatenates filter parameters from a given object.
*
* This function takes an object containing filter settings (either SeverityFilter
* or AlarmStatusSettings) and returns a string of all keys where the corresponding value is true.
* If the object is empty or null, an empty string is returned.
*
* @param obj - The object containing filter settings.
* @returns - A concatenated string of keys with true values, separated by commas.
*/
extractFilterParams(obj) {
if (!obj) {
return '';
}
return Object.entries(obj)
.filter(([, value]) => value)
.map(([key]) => key)
.join(',');
}
/**
* Determines if an incoming real-time alarm has a different status than an existing alarm.
*
* This function checks if the provided incoming real-time alarm's status differs
* from that of an existing alarm with the same ID in the given array of alarms.
*
* @param existingAlarms - The array of existing alarms.
* @param incomingRealtimeAlarm - The incoming real-time alarm to check.
* @returns - True if the existing alarm's status has changed, otherwise false.
*/
hasExistingAlarmChangedStatus(existingAlarms, incomingRealtimeAlarm) {
const existingAlarm = existingAlarms.find(alarm => alarm.id === incomingRealtimeAlarm.id);
return !!existingAlarm && existingAlarm.status !== incomingRealtimeAlarm.status;
}
/**
* Filters alarms based on their status, severity, and type.
*
* This method determines if a given alarm, identified either by a numeric ID or an `IAlarm` object,
* matches specific criteria defined in `alarms` and optionally `config`.
*
* @param alarm - The alarm to check, represented either by a numeric ID or an `IAlarm` object.
* @param alarms - An array of `IAlarm` objects against which the given alarm is evaluated.
* @param config - Optional. Configuration for the alarm list widget, used to define additional
* filtering criteria.
*
* @returns `true` if the alarm matches the specified criteria; otherwise, `false`.
* If `alarm` is a number, it always returns `false`.
* If `config` is not provided, it uses a legacy filter for critical alarms.
*
* @remarks
* - When `alarm` is a numeric ID, the function returns `false` as it cannot match against type and severity.
* - If `config` is not provided, the function assumes a legacy scenario for filtering all critical alarms.
*/
filterAlarmsByStatusSeverityAndType(alarm, alarms, config) {
if (typeof alarm === 'number') {
return false;
}
return this.isAlarmMatchedByConfig(alarm, alarms, config);
}
/**
* Determines if all values in the given object are false.
*
* This function checks every value in the provided object to see if they are all false.
*
* @param obj - An object with boolean values.
* @returns - Returns `true` if all values in the object are false, otherwise `false`.
*/
allValuesFalse(obj) {
return Object.values(obj).every(value => !value);
}
/**
* Constructs a string of order parameters for a query based on the specified alarm order.
*
* This function takes an alarm order and maps it to a corresponding set of order parameters.
* It supports different ordering types, such as BY_ACTIVE, BY_SEVERITY, and BY_DATE_ASCENDING.
* The order parameters are used to construct a query string that determines the order
* in which alarms are retrieved or displayed.
*
* @param order - The specified order for sorting alarms (e.g., BY_ACTIVE).
* @returns - A string of order parameters to be used in a query, or an empty string if the order type is unrecognized.
*/
getOrderParameters(order) {
let orderParams;
switch (order) {
case ALARM_ORDER_VALUES.BY_ACTIVE:
orderParams = ['status asc', 'severity asc', 'time.date desc', 'text asc'];
return this.buildOrderParameters(orderParams);
case ALARM_ORDER_VALUES.BY_SEVERITY:
orderParams = ['severity asc', 'time.date desc', 'text asc'];
return this.buildOrderParameters(orderParams);
case ALARM_ORDER_VALUES.BY_DATE_ASCENDING:
orderParams = ['time.date asc', 'text asc'];
return this.buildOrderParameters(orderParams);
default:
orderParams = ['time.date desc', 'text asc'];
return this.buildOrderParameters(orderParams);
}
}
/**
* Determines if an alarm is matched by the specified widget configuration.
*
* This function evaluates whether a given alarm should be included based on the severity,
* status, and type filters defined in the AlarmListWidgetConfig. It checks if the alarm's
* severity and status match the configuration settings and if the alarm's type is included
* in the configuration's types (if specified).
*
* @ignore
* @param alarm - The alarm to evaluate.
* @param alarms - An array of existing alarms, used for status matching.
* @param config - The configuration settings to match against.
* @returns - Returns `true` if the alarm matches the configuration criteria; otherwise, `false`.
*/
isAlarmMatchedByConfig(alarm, alarms, config) {
const isSeverityMatched = this.isSeverityMatching(alarm.severity, config);
const isStatusMatched = this.isStatusMatching(alarm, alarms, config);
const isTypeMatched = this.isTypesMatching(config, alarm);
return isSeverityMatched && isStatusMatched && isTypeMatched;
}
/**
* Checks if the severity of an alarm matches the configuration setting.
*
* This function determines whether the severity of an alarm is included in the
* severity settings defined in the AlarmListWidgetConfig.
*
* @ignore
* @param severity - The severity of the alarm to check.
* @param config - The configuration with severity settings.
* @returns - Returns `true` if the alarm's severity matches the configuration; otherwise, `false`.
*/
isSeverityMatching(severity, config) {
return !!config.severities[severity];
}
/**
* Evaluates if the status of an alarm matches the configuration setting or has changed.
*
* This function checks if the status of an alarm is included in the status settings defined in
* the AlarmListWidgetConfig, or if the alarm's status has changed based on the existing alarms.
*
* @ignore
* @param alarm - The alarm whose status is to be evaluated.
* @param alarms - An array of existing alarms to compare against for status changes.
* @param config - The configuration with status settings.
* @returns - Returns `true` if the alarm's status matches or has changed as per the configuration; otherwise, `false`.
*/
isStatusMatching(alarm, alarms, config) {
return !!config.status[alarm.status] || this.hasExistingAlarmChangedStatus(alarms, alarm);
}
/**
* Checks if the configuration's types array contains only empty string or includes a specific alarm type.
*
* @param config - The configuration object with a `types` property.
* @param alarm - The alarm object with a `type` property to check against the config's types.
* @returns `true` if the config's types array contains only empty string or includes the alarm's type, otherwise `false`.
*/
isTypesMatching(config, alarm) {
return Array.isArray(config.types) && config.types.length === 1 && config.types[0] === ''
? true
: (config.types?.includes(alarm.type) ?? false);
}
/**
* Constructs a query string from an array of order parameters.
*
* This function takes an array of ordering parameters and constructs a query string
* for use in alarm ordering queries. The parameters are concatenated into a single string,
* prefixed with '$orderby='.
*
* @ignore
* @private
* @param orderParams - The order parameters to be included in the query.
* @returns - A query string representing the order parameters.
*/
buildOrderParameters(orderParams) {
return `$orderby=${orderParams.join(',')}`;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AlarmWidgetService, deps: [{ token: i1.AlarmsViewService }], target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AlarmWidgetService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AlarmWidgetService, decorators: [{
type: Injectable,
args: [{ providedIn: 'root' }]
}], ctorParameters: () => [{ type: i1.AlarmsViewService }] });
class SeverityIconPipe {
transform(severity) {
let severityClassName = '';
let iconClassName = '';
switch (severity) {
case Severity.CRITICAL:
severityClassName = 'critical';
iconClassName = 'exclamation-circle';
break;
case Severity.MAJOR:
severityClassName = 'major';
iconClassName = 'warning';
break;
case Severity.MINOR:
severityClassName = 'minor';
iconClassName = 'high-priority';
break;
case Severity.WARNING:
severityClassName = 'warning';
iconClassName = 'circle';
break;
default:
return { iconClass: '', severityClass: '' };
}
return {
iconClass: `status icon-lg stroked-icon dlt-c8y-icon-${iconClassName} ${severityClassName}`,
c8yIcon: severityClassName
};
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: SeverityIconPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "19.2.14", ngImport: i0, type: SeverityIconPipe, isStandalone: true, name: "severityIcon" }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: SeverityIconPipe, decorators: [{
type: Pipe,
args: [{ name: 'severityIcon' }]
}] });
class SortingDescriptionPopoverMessagePipe {
transform(order) {
switch (order) {
case ALARM_ORDER_VALUES.BY_ACTIVE:
return gettext('Order alarms by active status, severity, and time.');
case ALARM_ORDER_VALUES.BY_DATE_ASCENDING:
return gettext('Order alarms by time, starting with the oldest ones.');
case ALARM_ORDER_VALUES.BY_DATE_DESCENDING:
return gettext('Order alarms by time, starting with the latest ones.');
case ALARM_ORDER_VALUES.BY_SEVERITY:
return gettext('Order alarms by severity and time.');
default:
return '';
}
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: SortingDescriptionPopoverMessagePipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "19.2.14", ngImport: i0, type: SortingDescriptionPopoverMessagePipe, isStandalone: true, name: "sortingDescriptionPopoverMessage" }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: SortingDescriptionPopoverMessagePipe, decorators: [{
type: Pipe,
args: [{ name: 'sortingDescriptionPopoverMessage' }]
}] });
class AlarmListWidgetConfigComponent {
set previewMapSet(template) {
if (template) {
this.widgetConfigService.setPreview(template);
return;
}
this.widgetConfigService.setPreview(null);
}
constructor(alarmListWidgetService, alarmService, alarmsViewService, alertService, form, formBuilder, translateService, widgetConfigService) {
this.alarmListWidgetService = alarmListWidgetService;
this.alarmService = alarmService;
this.alarmsViewService = alarmsViewService;
this.alertService = alertService;
this.form = form;
this.formBuilder = formBuilder;
this.translateService = translateService;
this.widgetConfigService = widgetConfigService;
this.REFRESH_INTERVAL_IN_MILLISECONDS = this.alarmsViewService.DEFAULT_INTERVAL_VALUES;
this.SEVERITY_LABELS = SEVERITY_LABELS;
this.STATUS_LABELS = ALARM_STATUS_LABELS;
this.ACKNOWLEDGE_STATUS_VALUE = AlarmStatus.ACKNOWLEDGED;
this.CLEARED_STATUS_VALUE = AlarmStatus.CLEARED;
this.BELL_SLASH_ICON = ALARM_STATUS_ICON.BELL_SLASH;
this.BELL_ICON = ALARM_STATUS_ICON.BELL;
this.C8Y_ALERT_IDLE_ICON = ALARM_STATUS_ICON.ALERT_IDLE;
this.ALARM_ORDER_LABELS = ALARM_ORDER_LABELS;
this.GLOBAL_INTERVAL_OPTION = GLOBAL_INTERVAL_OPTION;
this.alarms$ = new BehaviorSubject(null);
this.orderList = Object.values(ALARM_ORDER_VALUES);
this.severityList = Object.keys(SEVERITY_LABELS);
this.showDateFilter = true;
this.DATE_SELECTION = DATE_SELECTION;
this.dateSelectionHelp = this.translateService.instant(gettext(`Choose how to select a date range, the available options are:
<ul class="m-l-0 p-l-8 m-t-8 m-b-0">
<li>
<b>Widget configuration:</b>
restricts the date selection only to the widget configuration
</li>
<li>
<b>Widget and widget configuration:</b>
restricts the date selection to the widget view and widget configuration only
</li>
<li>
<b>Dashboard time range:</b>
restricts date selection to the global dashboard configuration only
</li>
</ul>`));
/**
* Order does matter.
*/
this.statusList = [AlarmStatus.ACTIVE, AlarmStatus.ACKNOWLEDGED, AlarmStatus.CLEARED];
}
async ngOnInit() {
this.refreshTypeTitle = this.config.isRealtime
? this.translateService.instant(gettext('Realtime'))
: this.translateService.instant(gettext('Auto refresh'));
this.refreshTypePopoverMessage = this.config.isRealtime
? this.translateService.instant(gettext('Change the default state of realtime refresh.'))
: this.translateService.instant(gettext('Change the state of interval automatic refresh and set the refresh frequency.'));
this.initializeForm();
if (!this.config.isRealtime) {
this.updateConfigBasedOnRefreshOption();
}
this.config.isRealtime = !this.alarmsViewService.isIntervalRefresh();
const isWidgetWithExistingConfig = this.config.order && this.config.severities && this.config.status;
if (isWidgetWithExistingConfig) {
await this.getAlarms(this.config);
}
else {
/** Used when creating a new widget */
await this.getAlarms(this.formGroup.value);
}
this.handlePreviewSubscription();
}
ngOnChanges(changes) {
if (this.formGroup && changes.config) {
this.formGroup.get('device').patchValue(this.config.device);
}
}
ngOnDestroy() {
this.formChangesSubscription?.unsubscribe();
}
onBeforeSave(config) {
const { types } = this.formGroup.value;
if (types.length > 1) {
const stringTypes = types;
if (this.isContainingOnlyEmptyTypes(stringTypes)) {
this.formGroup.value.types = [''];
}
else {
this.formGroup.value.types = this.filterEmptyTypes(stringTypes);
}
}
/**
* Applies only to converted legacy Alarm list widget
*/
if (config['options']) {
delete config['options'];
}
Object.assign(config, this.formGroup.value);
return true;
}
onDateFilterChange(event) {
this.formGroup.patchValue({
dateFilter: event.selectedDates,
interval: event.interval
});
}
removeType(index) {
if (this.types.controls.length === 1) {
this.formGroup.get('types').reset();
}
else {
this.types.removeAt(index);
}
}
addType() {
this.types.push(this.formBuilder.control(''));
}
updateRefreshOption() {
this.config.refreshOption = this.refreshOption;
this.formGroup.controls.refreshOption.setValue(this.refreshOption);
const isGlobalInterval = this.refreshOption === GLOBAL_INTERVAL_OPTION;
this.config.widgetInstanceGlobalAutoRefreshContext = isGlobalInterval;
this.formGroup.controls.widgetInstanceGlobalAutoRefreshContext.setValue(isGlobalInterval);
this.updateConfigBasedOnRefreshOption();
}
get types() {
return this.formGroup.get('types');
}
dateSelectionChange(dateSelection) {
if (dateSelection === DATE_SELECTION.CONFIG) {
this.showDateFilter = true;
this.formGroup.patchValue({
displayDateSelection: false,
widgetInstanceGlobalTimeContext: false
});
}
else if (dateSelection === DATE_SELECTION.VIEW_AND_CONFIG) {
this.showDateFilter = true;
this.formGroup.patchValue({
displayDateSelection: true,
widgetInstanceGlobalTimeContext: false
});
}
else if (dateSelection === DATE_SELECTION.DASHBOARD_CONTEXT) {
this.showDateFilter = false;
this.formGroup.patchValue({
displayDateSelection: false,
widgetInstanceGlobalTimeContext: true
});
}
}
filterEmptyTypes(types) {
return types.filter((element) => element !== '' && element !== null);
}
isContainingOnlyEmptyTypes(types) {
return types.every((element) => element === '' || element === null);
}
async getAlarms(config) {
try {
this.isLoading = true;
const result = await this.alarmService.list(this.alarmListWidgetService.mapConfigToQueryFilter(config, DEFAULT_PAGE_SIZE));
this.alarms$.next(result);
}
catch (error) {
this.alertService.addServerFailure(error);
}
finally {
this.isLoading = false;
}
}
initializeForm() {
this.formGroup = this.createForm();
this.form.form.addControl('config', this.formGroup);
this.formGroup.patchValue(this.config);
this.initializeTypes(this.config.types);
this.initDateSelection();
this.refreshOption = this.config.refreshOption ?? 'interval';
}
createForm() {
return this.formBuilder.group({
status: this.formBuilder.group(DEFAULT_STATUS_VALUES, {
validators: this.minSelectedCheckboxes(1)
}),
showAlarmsForChildren: true,
types: this.formBuilder.array([]),
severities: this.formBuilder.group(DEFAULT_SEVERITY_VALUES, {
validators: this.minSelectedCheckboxes(1)
}),
order: ALARM_ORDER_VALUES.BY_ACTIVE,
isAutoRefreshEnabled: [true],
refreshInterval: !this.config.isRealtime
? new FormControl({
value: this.alarmsViewService.DEFAULT_INTERVAL_VALUE,
disabled: this.isAutorefershDisabled()
})
: new FormControl(undefined),
device: this.config.device ? new FormControl(this.config.device) : new FormControl(undefined),
dateFilter: this.config.dateFilter
? new FormControl(this.config.dateFilter)
: new FormControl([new Date(0), new Date()]),
displayDateSelection: this.config.displayDateSelection || false,
widgetInstanceGlobalTimeContext: this.config.widgetInstanceGlobalTimeContext || false,
interval: this.config.interval || 'none',
refreshOption: this.config.isRealtime ? null : 'interval',
widgetInstanceGlobalAutoRefreshContext: this.config.refreshOption === GLOBAL_INTERVAL_OPTION
});
}
isAutorefershDisabled() {
/**This check is required on widget creation */
if (this.config.isAutoRefreshEnabled === undefined) {
return false;
}
return !this.config.isAutoRefreshEnabled;
}
minSelectedCheckboxes(min = 1) {
const validator = (formGroup) => {
const totalSelected = Object.values(formGroup.controls).reduce((prev, next) => (next.value ? prev + next.value : prev), 0);
return totalSelected >= min ? null : { required: true };
};
return validator;
}
initializeTypes(types) {
const typesControl = this.formGroup.get('types');
if (types) {
types.forEach(type => {
typesControl.push(this.formBuilder.control(type));
});
}
else {
typesControl.push(this.formBuilder.control(''));
}
}
initDateSelection() {
this.dateSelection = this.config?.widgetInstanceGlobalTimeContext
? DATE_SELECTION.DASHBOARD_CONTEXT
: this.config?.displayDateSelection
? DATE_SELECTION.VIEW_AND_CONFIG
: DATE_SELECTION.CONFIG;
this.dateSelectionChange(this.dateSelection);
const interval = this.config?.interval ?? 'none';
if (interval === 'none' || interval === 'custom') {
return;
}
this.config.dateFilter = this.alarmsViewService.getDateTimeContextByInterval(this.config.interval);
this.formGroup.patchValue({
dateFilter: this.config.dateFilter
});
}
/**
* Initializes and handles the form change subscription for the Alarm list preview at Widget's configuration.
*
* This method subscribes to form value changes with a debounce time of 500ms.
* It compares the previous and current form values to determine if the changes are relevant.
* If the only change is in the `isAutoRefreshEnabled` property, the change is ignored.
*/
handlePreviewSubscription() {
this.formChangesSubscription = this.formGroup.valueChanges
.pipe(startWith(this.formGroup.value), pairwise(), debounceTime(500))
.subscribe(([previousConfig, currentConfig]) => {
const typedPreviousConfig = previousConfig;
const typedCurrentConfig = currentConfig;
if (this.isOnlyAutoRefreshOrIntervalChanged(typedPreviousConfig, typedCurrentConfig)) {
return;
}
if (this.alarmListWidgetService.allValuesFalse(typedCurrentConfig.severities) ||
this.alarmListWidgetService.allValuesFalse(typedCurrentConfig.status)) {
this.alarms$.next({ data: [], res: null });
return;
}
this.getAlarms(typedCurrentConfig);
});
}
/**
* Determines if the only changes between two `AlarmListWidgetConfig` objects are in the `isAutoRefreshEnabled` and `refreshInterval` properties.
*
* This method compares two configurations by omitting `isAutoRefreshEnabled` and `refreshInterval`.
* It returns true if these are the only properties that differ between the previous and current configurations.
* Note that `refreshInterval` is relevant only when the refresh type is set to an interval.
*
* @param previousConfig - The previous configuration of `AlarmListWidgetConfig`.
* @param currentConfig - The current configuration of `AlarmListWidgetConfig`.
* @returns `true` if the differences are confined to `isAutoRefreshEnabled` and `refreshInterval`, `false` otherwise.
*/
isOnlyAutoRefreshOrIntervalChanged(previousConfig, currentConfig) {
const prevConfigCopy = omit(previousConfig, 'isAutoRefreshEnabled', 'refreshInterval');
const currentConfigCopy = omit(currentConfig, 'isAutoRefreshEnabled', 'refreshInterval');
return isEqual(prevConfigCopy, currentConfigCopy);
}
updateConfigBasedOnRefreshOption() {
const isInterval = this.refreshOption === 'interval';
this.config.isAutoRefreshEnabled = isInterval;
this.formGroup.controls.isAutoRefreshEnabled.setValue(isInterval);
isInterval
? this.formGroup.get('refreshInterval').enable()
: this.formGroup.get('refreshInterval').disable();
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AlarmListWidgetConfigComponent, deps: [{ token: AlarmWidgetService }, { token: i2.AlarmService }, { token: i1.AlarmsViewService }, { token: i2$1.AlertService }, { token: i5.NgForm }, { token: i5.FormBuilder }, { token: i6.TranslateService }, { token: i7.WidgetConfigService }], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.14", type: AlarmListWidgetConfigComponent, isStandalone: true, selector: "c8y-alarm-list-widget-config", inputs: { config: "config" }, viewQueries: [{ propertyName: "previewMapSet", first: true, predicate: ["alarmListPreview"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div\n class=\"p-l-24 p-r-24\"\n [style.pointer-events]=\"'auto'\"\n [style.opacity]=\"1\"\n></div>\n\n<form\n class=\"row d-flex flex-wrap\"\n [formGroup]=\"formGroup\"\n>\n <div class=\"col-xs-12\">\n <fieldset class=\"c8y-fieldset\">\n <legend>{{ 'Date' | translate }}</legend>\n <div class=\"form-group form-group-sm m-b-16\">\n <label class=\"d-flex a-i-center\">\n <span translate>Date selection</span>\n <button\n class=\"btn-help btn-help--sm\"\n [attr.aria-label]=\"'Help' | translate\"\n [popover]=\"dateSelectionHelpTemplate\"\n placement=\"bottom\"\n triggers=\"focus\"\n container=\"body\"\n [adaptivePosition]=\"false\"\n ></button>\n </label>\n <div class=\"c8y-select-wrapper\">\n <select\n class=\"form-control input-sm\"\n [ngModel]=\"dateSelection\"\n (ngModelChange)=\"dateSelectionChange($event)\"\n [ngModelOptions]=\"{ standalone: true }\"\n >\n <option\n title=\"{{ 'Widget configuration' | translate }}\"\n [value]=\"DATE_SELECTION.CONFIG\"\n >\n {{ 'Widget configuration' | translate }}\n </option>\n <option\n title=\"{{ 'Widget and widget configuration' | translate }}\"\n [value]=\"DATE_SELECTION.VIEW_AND_CONFIG\"\n >\n {{ 'Widget and widget configuration' | translate }}\n </option>\n <option\n title=\"{{ 'Dashboard time range' | translate }}\"\n [value]=\"DATE_SELECTION.DASHBOARD_CONTEXT\"\n >\n {{ 'Dashboard time range' | translate }}\n </option>\n </select>\n <span></span>\n </div>\n </div>\n <div class=\"row tight-grid\">\n <div class=\"col-md-7\">\n <ng-container *ngIf=\"showDateFilter\">\n <c8y-form-group class=\"m-b-16 form-group-sm\">\n <label>{{ 'Date filter' | translate }}</label>\n <c8y-alarms-date-filter\n [updateQueryParams]=\"false\"\n [DEFAULT_INTERVAL]=\"config.interval || 'none'\"\n formControlName=\"dateFilter\"\n (dateFilterChange)=\"onDateFilterChange($event)\"\n ></c8y-alarms-date-filter>\n </c8y-form-group>\n </ng-container>\n </div>\n <div class=\"col-md-5\">\n <c8y-form-group class=\"m-b-16 form-group-sm\">\n <label class=\"d-flex a-i-center\">\n {{ refreshTypeTitle }}\n <button\n class=\"btn-help btn-help--sm\"\n [attr.aria-label]=\"'Help' | translate\"\n [popover]=\"refreshTypePopoverMessage\"\n placement=\"top\"\n triggers=\"focus\"\n container=\"body\"\n type=\"button\"\n [adaptivePosition]=\"true\"\n ></button>\n </label>\n <div class=\"d-flex gap-8 a-i-center\">\n <ng-template #realtime>\n <label class=\"c8y-switch\">\n <input\n id=\"refreshToggle\"\n name=\"isAutoRefreshEnabled\"\n type=\"checkbox\"\n formControlName=\"isAutoRefreshEnabled\"\n />\n <span></span>\n <span class=\"sr-only\">{{ 'Realtime refresh' | translate }}</span>\n </label>\n </ng-template>\n <div\n class=\"c8y-select-wrapper\"\n *ngIf=\"!config.isRealtime; else realtime\"\n >\n <select\n class=\"form-control\"\n title=\"{{ 'Refresh options`options for refreshing a view`' | translate }}\"\n [(ngModel)]=\"refreshOption\"\n [ngModelOptions]=\"{ standalone: true }\"\n (change)=\"updateRefreshOption()\"\n >\n <option value=\"none\">\n {{ 'No automatic refresh' | translate }}\n </option>\n <option\n [title]=\"'Refreshing after the given interval' | translate\"\n value=\"interval\"\n >\n {{ 'Use refresh interval' | translate }}\n </option>\n <option\n [title]=\"'Refreshing after the given interval, synchronized globally' | translate\"\n value=\"global-interval\"\n >\n {{ 'Use global refresh interval' | translate }}\n </option>\n </select>\n </div>\n <ng-container\n *ngIf=\"!config.isRealtime && config.refreshOption !== GLOBAL_INTERVAL_OPTION\"\n >\n <div class=\"c8y-select-wrapper flex-grow flex-no-shrink\" title=\" {{ 'Interval' | translate }}\">\n <select\n class=\"form-control text-12\"\n [title]=\"'Refresh interval in seconds' | translate\"\n id=\"refreshInterval\"\n formControlName=\"refreshInterval\"\n data-cy=\"c8y-alarm-list-widget-config--interval-selector\"\n >\n <option\n *ngFor=\"let refreshInterval of REFRESH_INTERVAL_IN_MILLISECONDS\"\n [ngValue]=\"refreshInterval\"\n >\n {{ '{{ seconds }}s' | translate: { seconds: refreshInterval / 1000 } }}\n </option>\n </select>\n </div>\n </ng-container>\n </div>\n </c8y-form-group>\n </div>\n </div>\n </fieldset>\n </div>\n\n <div class=\"col-md-6 col-xs-12\">\n <fieldset class=\"c8y-fieldset\">\n <legend>{{ 'Order`of items on a list, noun`' | translate }}</legend>\n <c8y-form-group class=\"m-b-8\">\n <div\n class=\"d-flex p-b-8 p-t-4 a-i-center\"\n *ngFor=\"let order of orderList; let i = index\"\n data-cy=\"c8y-alarm-list-widget-config--order-elements\"\n >\n <label\n class=\"c8y-radio gap-4\"\n title=\"{{ ALARM_ORDER_LABELS[order] | translate }}\"\n >\n <input\n type=\"radio\"\n [value]=\"order\"\n formControlName=\"order\"\n />\n <span class=\"a-s-center\"></span>\n <span class=\"text-truncate\">{{ ALARM_ORDER_LABELS[order] | translate }}</span>\n </label>\n <button\n class=\"btn-help btn-help--sm\"\n [attr.aria-label]=\"'Help' | translate\"\n [popover]=\"order | sortingDescriptionPopoverMessage | translate\"\n placement=\"right\"\n triggers=\"focus\"\n container=\"body\"\n type=\"button\"\n [adaptivePosition]=\"true\"\n ></button>\n </div>\n </c8y-form-group>\n </fieldset>\n </div>\n <div class=\"col-md-6 col-xs-12\">\n <fieldset\n class=\"c8y-fieldset\"\n formArrayName=\"severities\"\n >\n <legend>{{ 'Severities' | translate }}</legend>\n <c8y-form-group\n [hasError]=\"formGroup.controls.severities.hasError('required')\"\n [ngClass]=\"{\n 'm-b-8': !formGroup.controls.severities.hasError('required')\n }\"\n data-cy=\"c8y-alarm-list-widget-config--severities-elements\"\n >\n <ng-container *ngFor=\"let severityOption of severityList\">\n <label\n class=\"c8y-checkbox m-t-0 gap-4\"\n [title]=\"SEVERITY_LABELS[severityOption] | translate\"\n >\n <input\n type=\"checkbox\"\n [formControlName]=\"severityOption\"\n [name]=\"severityOption\"\n />\n <span class=\"a-s-center\"></span>\n <ng-container *ngIf=\"{ result: severityOption | severityIcon } as severityIcon\">\n <i\n class=\"a-s-center m-r-4 icon-20 {{ severityIcon.result.iconClass }}\"\n [c8yIcon]=\"severityIcon.result.c8yIcon\"\n ></i>\n </ng-container>\n <span>{{ SEVERITY_LABELS[severityOption] | translate }}</span>\n </label>\n </ng-container>\n <c8y-messages>\n <c8y-message *ngIf=\"formGroup.controls.severities.hasError('required')\">\n {{ 'Select at least one severity.' | translate }}\n </c8y-message>\n </c8y-messages>\n </c8y-form-group>\n </fieldset>\n <ng-container *ngIf=\"showDateFilter\">\n <fieldset class=\"c8y-fieldset\">\n <legend>{{ 'Date filter' | translate }}</legend>\n <c8y-form-group class=\"m-b-8\">\n <c8y-alarms-date-filter\n [updateQueryParams]=\"false\"\n [DEFAULT_INTERVAL]=\"config.interval || 'none'\"\n formControlName=\"dateFilter\"\n (dateFilterChange)=\"onDateFilterChange($event)\"\n ></c8y-alarms-date-filter>\n </c8y-form-group>\n </fieldset>\n </ng-container>\n </div>\n\n <div class=\"col-md-6 col-xs-12\">\n <fieldset\n class=\"c8y-fieldset\"\n formArrayName=\"status\"\n >\n <legend data-cy=\"c8y-alarm-list-widget-config-status-label\">\n {{ 'Status' | translate }}\n </legend>\n <c8y-form-group\n [hasError]=\"formGroup.controls.status.hasError('required')\"\n [ngClass]=\"{\n 'm-b-8': !formGroup.controls.status.hasError('required')\n }\"\n data-cy=\"c8y-alarm-list-widget-config--status-elements\"\n >\n <ng-container *ngFor=\"let statusOption of statusList\">\n <label\n class=\"c8y-checkbox m-t-0 gap-4\"\n [title]=\"STATUS_LABELS[statusOption] | translate\"\n >\n <input\n type=\"checkbox\"\n [formControlName]=\"statusOption\"\n [name]=\"statusOption\"\n />\n <span class=\"a-s-center\"></span>\n <i\n class=\"a-s-center m-l-4 m-r-4 text-gray-dark c8y-icon icon-20\"\n [c8yIcon]=\"\n statusOption === CLEARED_STATUS_VALUE\n ? C8Y_ALERT_IDLE_ICON\n : statusOption === ACKNOWLEDGE_STATUS_VALUE\n ? BELL_SLASH_ICON\n : BELL_ICON\n \"\n ></i>\n <span>{{ STATUS_LABELS[statusOption] | translate }}</span>\n </label>\n </ng-container>\n <c8y-messages>\n <c8y-message *ngIf=\"formGroup.controls.status.hasError('required')\">\n {{ 'Select at least one status.' | translate }}\n </c8y-message>\n </c8y-messages>\n </c8y-form-group>\n </fieldset>\n </div>\n <div class=\"col-md-6 col-xs-12\">\n <fieldset\n class=\"c8y-fieldset\"\n data-cy=\"c8y-alarm-list-widget-config--child-devices-section\"\n >\n <legend>{{ 'Child devices' | translate }}</legend>\n <c8y-form-group class=\"m-b-8\">\n <label\n class=\"c8y-switch\"\n [title]=\"'Show alarms from child devices' | translate\"\n data-cy=\"c8y-alarm-list-widget-config--child-devices-label\"\n >\n <input\n type=\"checkbox\"\n formControlName=\"showAlarmsForChildren\"\n data-cy=\"c8y-alarm-list-widget-config--showAlarmsForChildren-checkbox\"\n />\n <span></span>\n <span>{{ 'Show alarms' | translate }}</span>\n <span class=\"sr-only\">{{ 'Show alarms' | translate }}</span>\n </label>\n </c8y-form-group>\n </fieldset>\n </div>\n \n <div class=\"col-xs-12\">\n <fieldset class=\"c8y-fieldset\">\n <legend>{{ 'Types' | translate }}</legend>\n <c8y-form-group\n class=\"m-b-8\"\n formArrayName=\"types\"\n >\n <div\n class=\"input-group m-b-4\"\n *ngFor=\"let type of types.controls; let i = index\"\n data-cy=\"c8y-alarm-list-widget-config--types-elements\"\n >\n <input\n class=\"form-control\"\n type=\"text\"\n [placeholder]=\"'e.g. {{ example }}' | translate: { example: 'c8y_Alarm' }\"\n [formControlName]=\"i\"\n />\n <div class=\"input-group-btn\">\n <button\n class=\"btn-dot btn-dot--danger m-l-auto\"\n [title]=\"'Remove' | translate\"\n type=\"button\"\n (click)=\"removeType(i)\"\n data-cy=\"c8y-alarm-list-widget-config--types-remove-type\"\n [disabled]=\"types.controls?.length === 1 && !type.value\"\n >\n <i c8yIcon=\"minus-circle\"></i>\n </button>\n </div>\n <div\n class=\"input-group-btn\"\n *ngIf=\"i === types.controls.length - 1\"\n >\n <button\n class=\"btn-dot btn-dot--primary m-l-auto\"\n [title]=\"'Add alarm type' | translate\"\n type=\"button\"\n (click)=\"addType()\"\n data-cy=\"c8y-alarm-list-widget-config--types-add-type\"\n >\n <i c8yIcon=\"plus-circle\"></i>\n </button>\n </div>\n </div>\n </c8y-form-group>\n </fieldset>\n </div>\n</form>\n\n<ng-template #alarmListPreview>\n <c8y-alarms-list\n [alarms]=\"alarms$ | async\"\n [isInitialLoading]=\"isLoading\"\n [navigationOptions]=\"{\n allowNavigationToAlarmsView: false,\n alwaysNavigateToAllAlarms: false,\n includeClearedQueryParams: false,\n queryParamsHandling: 'merge'\n }\"\n data-cy=\"c8y-alarm-list-widget-config--preview-alarm-list\"\n ></c8y-alarms-list>\n</ng-template>\n\n<ng-template #dateSelectionHelpTemplate>\n <div [innerHTML]=\"dateSelectionHelp\"></div>\n</ng-template>\n", dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i5.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i5.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i5.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i5.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: i5.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i5.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i5.RadioControlValueAccessor, selector: "input[type=radio][formControlName],input[type=radio][formControl],input[type=radio][ngModel]", inputs: ["name", "formControlName", "value"] }, { kind: "directive", type: i5.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i5.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i5.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i5.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i5.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "directive", type: i5.FormArrayName, selector: "[formArrayName]", inputs: ["formArrayName"] }, { kind: "directive", type: C8yTranslateDirective, selector: "[translate],[ngx-translate]" }, { 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: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: FormGroupComponent, selector: "c8y-form-group", inputs: ["hasError", "hasWarning", "hasSuccess", "novalidation", "status"] }, { kind: "component", type: AlarmsDateFilterComponent, selector: "c8y-alarms-date-filter", inputs: ["DEFAULT_INTERVAL", "updateQueryParams", "date"], outputs: ["dateFilterChange"] }, { kind: "directive", type: RequiredInputPlaceholderDirective, selector: "input[required], input[formControlName]" }, { kind: "directive", type: NgFor, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "component", type: MessagesComponent, selector: "c8y-messages", inputs: ["show", "defaults", "helpMessage"] }, { kind: "directive", type: MessageDirective, selector: "c8y-message", inputs: ["name", "text"] }, { kind: "component", type: AlarmsListComponent, selector: "c8y-alarms-list", inputs: ["alarms", "hasPermissions", "typeFilters", "loadMoreMode", "navigationOptions", "isInitialLoading", "splitView"], outputs: ["onSelectedAlarm", "onScrollingStateChange"] }, { kind: "pipe", type: C8yTranslatePipe, name: "translate" }, { kind: "pipe", type: AsyncPipe, name: "async" }, { kind: "pipe", type: SeverityIconPipe, name: "severityIcon" }, { kind: "pipe", type: SortingDescriptionPopoverMessagePipe, name: "sortingDescriptionPopoverMessage" }], viewProviders: [{ provide: ControlContainer, useExisting: NgForm }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AlarmListWidgetConfigComponent, decorators: [{
type: Component,
args: [{ selector: 'c8y-alarm-list-widget-config', viewProviders: [{ provide: ControlContainer, useExisting: NgForm }], imports: [
FormsModule,
ReactiveFormsModule,
C8yTranslateDirective,
PopoverDirective,
NgIf,
FormGroupComponent,
AlarmsDateFilterComponent,
RequiredInputPlaceholderDirective,
NgFor,
NgClass,
IconDirective,
MessagesComponent,
MessageDirective,
AlarmsListComponent,
C8yTranslatePipe,
AsyncPipe,
SeverityIconPipe,
SortingDescriptionPopoverMessagePipe
], template: "<div\n class=\"p-l-24 p-r-24\"\n [style.pointer-events]=\"'auto'\"\n [style.opacity]=\"1\"\n></div>\n\n<form\n class=\"row d-flex flex-wrap\"\n [formGroup]=\"formGroup\"\n>\n <div class=\"col-xs-12\">\n <fieldset class=\"c8y-fieldset\">\n <legend>{{ 'Date' | translate }}</legend>\n <div class=\"form-group form-group-sm m-b-16\">\n <label class=\"d-flex a-i-center\">\n <span translate>Date selection</span>\n <button\n class=\"btn-help btn-help--sm\"\n [attr.aria-label]=\"'Help' | translate\"\n [popover]=\"dateSelectionHelpTemplate\"\n placement=\"bottom\"\n triggers=\"focus\"\n container=\"body\"\n [adaptivePosition]=\"false\"\n ></button>\n </label>\n <div class=\"c8y-select-wrapper\">\n <select\n class=\"form-control input-sm\"\n [ngModel]=\"dateSelection\"\n (ngModelChange)=\"dateSelectionChange($event)\"\n [ngModelOptions]=\"{ standalone: true }\"\n >\n <option\n title=\"{{ 'Widget configuration' | translate }}\"\n [value]=\"DATE_SELECTION.CONFIG\"\n >\n {{ 'Widget configuration' | translate }}\n </option>\n <option\n title=\"{{ 'Widget and widget configuration' | translate }}\"\n [value]=\"DATE_SELECTION.VIEW_AND_CONFIG\"\n >\n {{ 'Widget and widget configuration' | translate }}\n </option>\n <option\n title=\"{{ 'Dashboard time range' | translate }}\"\n [value]=\"DATE_SELECTION.DASHBOARD_CONTEXT\"\n >\n {{ 'Dashboard time range' | translate }}\n </option>\n </select>\n <span></span>\n </div>\n </div>\n <div class=\"row tight-grid\">\n <div class=\"col-md-7\">\n <ng-container *ngIf=\"showDateFilter\">\n <c8y-form-group class=\"m-b-16 form-group-sm\">\n <label>{{ 'Date filter' | translate }}</label>\n <c8y-alarms-date-filter\n [updateQueryParams]=\"false\"\n [DEFAULT_INTERVAL]=\"config.interval || 'none'\"\n formControlName=\"dateFilter\"\n (dateFilterChange)=\"onDateFilterChange($event)\"\n ></c8y-alarms-date-filter>\n </c8y-form-group>\n </ng-container>\n </div>\n <div class=\"col-md-5\">\n <c8y-form-group class=\"m-b-16 form-group-sm\">\n <label class=\"d-flex a-i-center\">\n {{ refreshTypeTitle }}\n <button\n class=\"btn-help btn-help--sm\"\n [attr.aria-label]=\"'Help' | translate\"\n [popover]=\"refreshTypePopoverMessage\"\n placement=\"top\"\n triggers=\"focus\"\n container=\"body\"\n type=\"button\"\n [adaptivePosition]=\"true\"\n ></button>\n </label>\n <div class=\"d-flex gap-8 a-i-center\">\n <ng-template #realtime>\n <label class=\"c8y-switch\">\n <input\n id=\"refreshToggle\"\n name=\"isAutoRefreshEnabled\"\n type=\"checkbox\"\n formControlName=\"isAutoRefreshEnabled\"\n />\n <span></span>\n <span class=\"sr-only\">{{ 'Realtime refresh' | translate }}</span>\n </label>\n </ng-template>\n <div\n class=\"c8y-select-wrapper\"\n *ngIf=\"!config.isRealtime; else realtime\"\n >\n <select\n class=\"form-control\"\n title=\"{{ 'Refresh options`options for refreshing a view`' | translate }}\"\n [(ngModel)]=\"refreshOption\"\n [ngModelOptions]=\"{ standalone: true }\"\n (change)=\"updateRefreshOption()\"\n >\n <option value=\"none\">\n {{ 'No automatic refresh' | translate }}\n </option>\n <option\n [title]=\"'Refreshing after the given interval' | translate\"\n value=\"interval\"\n >\n {{ 'Use refresh interval' | translate }}\n </option>\n <option\n [title]=\"'Refreshing after the given interval, synchronized globally' | translate\"\n value=\"global-interval\"\n >\n {{ 'Use global refresh interval' | translate }}\n </option>\n </select>\n </div>\n <ng-container\n *ngIf=\"!config.isRealtime && config.refreshOption !== GLOBAL_INTERVAL_OPTION\"\n >\n <div class=\"c8y-select-wrapper flex-grow flex-no-shrink\" title=\" {{ 'Interval' | translate }}\">\n <select\n class=\"form-control text-12\"\n [title]=\"'Refresh interval in seconds' | translate\"\n id=\"refreshInterval\"\n formControlName=\"refreshInterval\"\n data-cy=\"c8y-alarm-list-widget-config--interval-selector\"\n >\n <option\n *ngFor=\"let refreshInterval of REFRESH_INTERVAL_IN_MILLISECONDS\"\n [ngValue]=\"refreshInterval\"\n >\n {{ '{{ seconds }}s' | translate: { seconds: refreshInterval / 1000 } }}\n </option>\n </select>\n </div>\n </ng-container>\n </div>\n </c8y-form-group>\n </div>\n </div>\n </fieldset>\n </div>\n\n <div class=\"col-md-6 col-xs-12\">\n <fieldset class=\"c8y-fieldset\">\n <legend>{{ 'Order`of items on a list, noun`' | translate }}</legend>\n <c8y-form-group class=\"m-b-8\">\n <div\n class=\"d-flex p-b-8 p-t-4 a-i-center\"\n *ngFor=\"let order of orderList; let i = index\"\n data-cy=\"c8y-alarm-list-widget-config--order-elements\"\n >\n <label\n class=\"c8y-radio gap-4\"\n title=\"{{ ALARM_ORDER_LABELS[order] | translate }}\"\n >\n <input\n type=\"radio\"\n [value]=\"order\"\n formControlName=\"order\"\n />\n <span class=\"a-s-center\"></span>\n <span class=\"text-truncate\">{{ ALARM_ORDER_LABELS[order] | translate }}</span>\n </label>\n <button\n class=\"btn-help btn-help--sm\"\n [attr.aria-label]=\"'Help' | translate\"\n [popover]=\"order | sortingDescriptionPopoverMessage | translate\"\n placement=\"right\"\n triggers=\"focus\"\n container=\"body\"\n type=\"button\"\n [adaptivePosition]=\"true\"\n ></button>\n </div>\n </c8y-form-group>\n </fieldset>\n </div>\n <div class=\"col-md-6 col-xs-12\">\n <fieldset\n class=\"c8y-fieldset\"\n formArrayName=\"severities\"\n >\n <legend>{{ 'Severities' | translate }}</legend>\n <c8y-form-group\n [hasError]=\"formGroup.controls.severities.hasError('required')\"\n [ngClass]=\"{\n 'm-b-8': !formGroup.controls.severities.hasError('required')\n }\"\n data-cy=\"c8y-alarm-list-widget-config--severities-elements\"\n >\n <ng-container *ngFor=\"let severityOption of severityList\">\n <label\n class=\"c8y-checkbox m-t-0 gap-4\"\n [title]=\"SEVERITY_LABELS[severityOption] | translate\"\n >\n <input\n type=\"checkbox\"\n [formControlName]=\"severityOption\"\n [name]=\"severityOption\"\n />\n <span class=\"a-s-center\"></span>\n <ng-container *ngIf=\"{ result: severityOption | severityIcon } as severityIcon\">\n <i\n class=\"a-s-center m-r-4 icon-20 {{ severityIcon.result.iconClass }}\"\n [c8yIcon]=\"severityIcon.result.c8yIcon\"\n ></i>\n </ng-container>\n <span>{{ SEVERITY_LABELS[severityOption] | translate }}</span>\n </label>\n </ng-container>\n <c8y-messages>\n <c8y-message *ngIf=\"formGroup.controls.severities.hasError('required')\">\n {{ 'Select at least one severity.' | translate }}\n </c8y-message>\n </c8y-messages>\n </c8y-form-group>\n </fieldset>\n <ng-container *ngIf=\"showDateFilter\">\n <fieldset class=\"c8y-fieldset\">\n <legend>{{ 'Date filter' | translate }}</legend>\n <c8y-form-group class=\"m-b-8\">\n <c8y-alarms-date-filter\n [updateQueryParams]=\"false\"\n [DEFAULT_INTERVAL]=\"config.interval || 'none'\"\n formControlName=\"dateFilter\"\n (dateFilterChange)=\"onDateFilterChange($event)\"\n ></c8y-alarms-date-filter>\n </c8y-form-group>\n </fieldset>\n </ng-container>\n </div>\n\n <div class=\"col-md-6 col-xs-12\">\n <fieldset\n class=\"c8y-fieldset\"\n formArrayName=\"status\"\n >\n <legend data-cy=\"c8y-alarm-list-widget-config-status-label\">\n {{ 'Status' | translate }}\n </legend>\n <c8y-form-group\n [hasError]=\"formGroup.controls.status.hasError('required')\"\n [ngClass]=\"{\n 'm-b-8': !formGroup.controls.status.hasError('required')\n }\"\n data-cy=\"c8y-alarm-list-widget-config--status-elements\"\n >\n <ng-container *ngFor=\"let statusOption of statusList\">\n <label\n class=\"c8y-checkbox m-t-0 gap-4\"\n [title]=\"STATUS_LABELS[statusOption] | translate\"\n >\n <input\n type=\"checkbox\"\n [formControlName]=\"statusOption\"\n [name]=\"statusOption\"\n />\n <span class=\"a-s-center\"></span>\n <i\n class=\"a-s-center m-l-4 m-r-4 text-gray-dark c8y-icon icon-20\"\n [c8yIcon]=\"\n statusOption === CLEARED_STATUS_VALUE\n ? C8Y_ALERT_IDLE_ICON\n : statusOption === ACKNOWLEDGE_STATUS_VALUE\n ? BELL_SLASH_ICON\n : BELL_ICON\n \"\n ></i>\n <span>{{ STATUS_LABELS[statusOption] | translate }}</span>\n </label>\n </ng-container>\n <c8y-messages>\n <c8y-message *ngIf=\"formGroup.controls.status.hasError('required')\">\n {{ 'Select at least one status.' | translate }}\n </c8y-message>\n </c8y-messages>\n </c8y-form-group>\n </fieldset>\n </div>\n <div class=\"col-md-6 col-xs-12\">\n <fieldset\n class=\"c8y-fieldset\"\n data-cy=\"c8y-alarm-list-widget-config--child-devices-section\"\n >\n <legend>{{ 'Child devices' | translate }}</legend>\n <c8y-form-group class=\"m-b-8\">\n <label\n class=\"c8y-switch\"\n [title]=\"'Show alarms from child devices' | translate\"\n data-cy=\"c8y-alarm-list-widget-config--child-devices-label\"\n >\n <input\n type=\"checkbox\"\n formControlName=\"showAlarmsForChildren\"\n data-cy=\"c8y-alarm-list-widget-config--showAlarmsForChildren-checkbox\"\n />\n <span></span>\n <span>{{ 'Show alarms' | translate }}</span>\n <span class=\"sr-only\">{{ 'Show alarms' | translate }}</span>\n </label>\n </c8y-form-group>\n </fieldset>\n </div>\n \n <div class=\"col-xs-12\">\n <fieldset class=\"c8y-fieldset\">\n <legend>{{ 'Types' | translate }}</legend>\n <c8y-form-group\n class=\"m-b-8\"\n formArrayName=\"types\"\n >\n <div\n class=\"input-group m-b-4\"\n *ngFor=\"let type of types.controls; let i = index\"\n data-cy=\"c8y-alarm-list-widget-config--types-elements\"\n >\n <input\n class=\"form-control\"\n type=\"text\"\n [placeholder]=\"'e.g. {{ example }}' | translate: { example: 'c8y_Alarm' }\"\n [formControlName]=\"i\"\n />\n <div class=\"input-group-btn\">\n <button\n class=\"btn-dot btn-dot--danger m-l-auto\"\n [title]=\"'Remove' | translate\"\n type=\"button\"\n (click)=\"removeType(i)\"\n data-cy=\"c8y-alarm-list-widget-config--types-remove-type\"\n [disabled]=\"types.controls?.length === 1 && !type.value\"\n >\n <i c8yIcon=\"minus-circle\"></i>\n </button>\n </div>\n <div\n class=\"input-group-btn\"\n *ngIf=\"i === types.controls.length - 1\"\n >\n <button\n class=\"btn-dot btn-dot--primary m-l-auto\"\n [title]=\"'Add alarm type' | translate\"\n type=\"button\"\n (click)=\"addType()\"\n data-cy=\"c8y-alarm-list-widget-config--types-add-type\"\n >\n <i c8yIcon=\"plus-circle\"></i>\n </button>\n </div>\n </div>\n </c8y-form-group>\n </fieldset>\n </div>\n</form>\n\n<ng-template #alarmListPreview>\n <c8y-alarms-list\n [alarms]=\"alarms$ | async\"\n [isInitialLoading]=\"isLoading\"\n [navigationOptions]=\"{\n allowNavigationToAlarmsView: false,\n alwaysNavigateToAllAlarms: false,\n includeClearedQueryParams: false,\n queryParamsHandling: 'merge'\n }\"\n data-cy=\"c8y-alarm-list-widget-config--preview-alarm-list\"\n ></c8y-alarms-list>\n</ng-template>\n\n<ng-template #dateSelectionHelpTemplate>\n <div [innerHTML]=\"dateSelectionHelp\"></div>\n</ng-template>\n" }]
}], ctorParameters: () => [{ type: AlarmWidgetService }, { type: i2.AlarmService }, { type: i1.AlarmsViewService }, { type: i2$1.AlertService }, { type: i5.NgForm }, { type: i5.FormBuilder }, { type: i6.TranslateService }, { type: i7.WidgetConfigService }], propDecorators: { config: [{
type: Input
}], previewMapSet: [{
type: ViewChild,
args: ['alarmListPreview']
}] } });
class AlarmWidgetAlarmsReloadComponent extends IntervalBasedReload {
constructor(alarmsViewService, cdRef, translateService, widgetGlobalAutoRefreshService, gainsightService) {
super();
this.alarmsViewService = alarmsViewService;
this.cdRef = cdRef;
this.translateService = translateService;
this.widgetGlobalAutoRefreshService = widgetGlobalAutoRefreshService;
this.gainsightService = gainsightService;
this.REALTIME_UPDATE_ALARMS_MESSAGE = this.alarmsViewService.REALTIME_UPDATE_ALARMS_MESSAGE;
this.WIDGET_TYPE_VALUES = WIDGET_TYPE_VALUES;
/**
* @inheritdoc
*/
this.onCountdownEnded = new EventEmitter();
/**
* Emitted to indicate that the widgets realtime button state has changed.
*/
this.onRealTimeToggleChanged = new EventEmitter();
/**
* Indicates whether the countdown has been cleared already.
*/
this.isNewAlarmMessageCleared = true;
/**
* @inheritdoc
*/
this.manuallyDisabledCountdown = false;
}
ngOnInit() {
if (!this.isIntervalRefresh) {
this.isRealtimeToggleOn = this.config.isAutoRefreshEnabled;
this.realtimeIconTitle = this.getRealtimeIconTitle();
}
else {
this.isIntervalRefreshToggleOn =
!this.isRefreshDisabled && (this.config.isAutoRefreshEnabled || !!this.refreshInterval);
this.updateCountdownButtonTooltipText();
}
}
ngAfterViewInit() {
if (this.isIntervalRefresh && this.refreshInterval) {
this.startCountdown();
}
if (this.globalAutoRefreshEnabled) {
this.widgetGlobalAutoRefreshService.countdownActions.countdownEnded$
.pipe(tap(() => this.onCountdownEnded.emit()))
.subscribe();
}
}
ngOnChanges(changes) {
const { isDisabled, config } = changes;
if (config) {
this.globalAutoRefreshEnabled = this.config.refreshOption === GLOBAL_INTERVAL_OPTION;
}
if (isDisabled) {
this.isIntervalRefreshToggleOn =
!this.isRefreshDisabled && (this.config.isAutoRefreshEnabled || !!this.refreshInterval);
this.updateCountdownButtonTooltipText(gettext('Disabled'));
}
if (!this.isIntervalRefresh ||
this.manuallyDisabledCountdown ||
!this.config.isAutoRefreshEnabled) {
return;
}
this.handleScrolling();
}
ngOnDestroy() {
if (this.countdownSubscription) {
this.countdownSubscription.unsubscribe();
}
}
/**
* @inheritdoc
*/
countdownEnded() {
/**
* @inheritdoc
*/
this.autoRefreshList();
}
reload() {
if (this.isIntervalRefresh) {
this.autoRefreshList();
}
else {
this.onCountdownEnded.emit();
this.isNewAlarmMessageCleared = true;
}
}
/**
* Toggles the real-time state of the component.
*
* This method switches the `isRealtimeToggleOn` state between true and false.
* When the state is toggled, it emits the current state through `onRealTimeToggleChanged` event.
* It also updates the `realtimeIconTitle` based on the new state.
*/
toggleRealtimeState() {
this.isRealtimeToggleOn = !this.isRealtimeToggleOn;
this.onRealTimeToggleChanged.emit(this.isRealtimeToggleOn);
this.realtimeIconTitle = this.getRealtimeIconTitle();
}
/**
* Stops the countdown and triggers a refresh action.
* This function is responsible for halting the countdown interval component's operation.
* After stopping the countdown, it emits an `onCountdownEnded` event.
* This event is used to inform external components that the countdown has ended,
* typically prompting them to reload or refresh their data.
*/
autoRefreshList() {
if (this.isIntervalRefreshToggleOn && this.config.isAutoRefreshEnabled) {
this.countdownIntervalComponent.stop();
}
this.onCountdownEnded.emit();
}
/**
* Sets the tooltip message for a new realtime alarm indicator.
* @returns Tooltip message.
*/
getRealtimeIconTitle() {
return this.isRealtimeToggleOn
? this.translateService.instant(gettext('Realtime active'))
: this.translateService.instant(gettext('Realtime inactive'));
}
/**
* Enables and starts the countdown timer.
*
* This method makes the countdown visible (`hideCountdown` is set to false) and then
* starts the countdown process. It ensures the countdown timer is updated immediately
* by triggering change detection with `cdRef.detectChanges()` before starting the countdown.
* This method encapsulates the logic required to initiate the countdown timer.
*/
enableCountdown() {
this.hideCountdown = false;
// Prevents the countdown from getting stuck on an initial value.
this.cdRef.detectChanges();
this.startCountdown();
}
/**
* @inheritdoc
*/
updateCountdownButtonTooltipText(customText) {
if (customText) {
this.toggleCountdownButtonTooltipText = customText;
return;
}
this.toggleCountdownButtonTooltipText = this.isIntervalRefreshToggleOn
? this.translateService.instant(gettext('Disable auto refresh'))
: this.translateService.instant(gettext('Enable auto refresh'));
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AlarmWidgetAlarmsReloadComponent, deps: [{ token: i1.AlarmsViewService }, { token: i0.ChangeDetectorRef }, { token: i6.TranslateService }, { token: i2$1.WidgetGlobalAutoRefreshService }, { token: i2$1.GainsightService }], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.14", type: AlarmWidgetAlarmsReloadComponent, isStandalone: true, selector: "c8y-alarm-widget-alarms-reload", inputs: { isIntervalRefresh: "isIntervalRefresh", refreshInterval: "refreshInterval", config: "config", isLoading: "isLoading", isScrolling: "isScrolling", isRefreshDisabled: "isRefreshDisabled" }, outputs: { onCountdownEnded: "onCountdownEnded", onRealTimeToggleChanged: "onRealTimeToggleChanged" }, viewQueries: [{ propertyName: "countdownIntervalComponent", first: true, predicate: CountdownIntervalComponent, descendants: true }], usesInheritance: true, usesOnChanges: true, ngImport: i0, template: "<div\n class=\"m-l-auto d-flex p-t-4 p-b-4\"\n *ngIf=\"!globalAutoRefreshEnabled\"\n>\n <div class=\"m-l-auto d-flex a-i-center\">\n <label\n class=\"m-b-0 m-r-8 text-label-small text-truncate flex-no-shrink\"\n title=\" {{ 'Auto refresh' | translate }}\"\n *ngIf=\"isIntervalRefresh && refreshInterval && config.isAutoRefreshEnabled\"\n >\n {{ 'Auto refresh' | translate }}\n </label>\n <div class=\"input-group\">\n <label\n class=\"toggle-countdown\"\n [class.toggle-countdown-disabled]=\"isRefreshDisabled\"\n [attr.aria-label]=\"toggleCountdownButtonTooltipText\"\n [tooltip]=\"toggleCountdownButtonTooltipText\"\n placement=\"bottom\"\n *ngIf=\"isIntervalRefresh && refreshInterval && config.isAutoRefreshEnabled\"\n [adaptivePosition]=\"false\"\n [container]=\"'body'\"\n [delay]=\"500\"\n >\n <input\n type=\"checkbox\"\n data-cy=\"c8y-alarms-widget--interval-toggle-button\"\n (click)=\"onToggleCountdownButtonState($event, WIDGET_TYPE_VALUES.ALARMS)\"\n />\n <c8y-countdown-interval\n *ngIf=\"isIntervalRefreshToggleOn\"\n [countdownInterval]=\"refreshInterval\"\n (countdownEnded)=\"countdownEnded()\"\n ></c8y-countdown-interval>\n <i\n c8yIcon=\"pause\"\n *ngIf=\"!isIntervalRefreshToggleOn\"\n ></i>\n </label>\n\n <div class=\"input-group-btn\">\n <button\n class=\"btn btn-default\"\n [attr.aria-label]=\"'Refresh' | translate\"\n [tooltip]=\"\n !isNewAlarmMessageCleared\n ? (REALTIME_UPDATE_ALARMS_MESSAGE | translate)\n : ('Refresh' | translate)\n \"\n placement=\"bottom\"\n type=\"button\"\n [adaptivePosition]=\"false\"\n [container]=\"'body'\"\n [delay]=\"500\"\n [disabled]=\"isRefreshDisabled || (isLoading | async)\"\n (click)=\"reload()\"\n data-cy=\"c8y-alarms-widget--reload-button\"\n >\n <span\n class=\"tag tag--info m-r-8\"\n *ngIf=\"!isNewAlarmMessageCleared\"\n >\n {{ 'New alarms' | translate }}\n </span>\n <i\n c8yIcon=\"refresh\"\n [ngClass]=\"{ 'icon-spin': isLoading | async }\"\n ></i>\n </button>\n </div>\n <div\n class=\"input-group-btn input-group-btn--last\"\n *ngIf=\"!isIntervalRefresh\"\n >\n <button\n class=\"c8y-realtime btn btn-default\"\n [attr.aria-label]=\"realtimeIconTitle\"\n [tooltip]=\"realtimeIconTitle\"\n placement=\"bottom\"\n type=\"button\"\n [container]=\"'body'\"\n (click)=\"toggleRealtimeState()\"\n [disabled]=\"isRefreshDisabled\"\n data-cy=\"c8y-alarms-widget--realtime-indicator\"\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 </div>\n </div>\n</div>", dependencies: [{ kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { 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: CountdownIntervalComponent, selector: "c8y-countdown-interval", inputs: ["countdownInterval"], outputs: ["countdownEnded"] }, { kind: "directive", type: IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { 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: AlarmWidgetAlarmsReloadComponent, decorators: [{
type: Component,
args: [{ selector: 'c8y-alarm-widget-alarms-reload', imports: [
NgIf,
TooltipDirective,
CountdownIntervalComponent,
IconDirective,
NgClass,
C8yTranslatePipe,
AsyncPipe
], template: "<div\n class=\"m-l-auto d-flex p-t-4 p-b-4\"\n *ngIf=\"!globalAutoRefreshEnabled\"\n>\n <div class=\"m-l-auto d-flex a-i-center\">\n <label\n class=\"m-b-0 m-r-8 text-label-small text-truncate flex-no-shrink\"\n title=\" {{ 'Auto refresh' | translate }}\"\n *ngIf=\"isIntervalRefresh && refreshInterval && config.isAutoRefreshEnabled\"\n >\n {{ 'Auto refresh' | translate }}\n </label>\n <div class=\"input-group\">\n <label\n class=\"toggle-countdown\"\n [class.toggle-countdown-disabled]=\"isRefreshDisabled\"\n [attr.aria-label]=\"toggleCountdownButtonTooltipText\"\n [tooltip]=\"toggleCountdownButtonTooltipText\"\n placement=\"bottom\"\n *ngIf=\"isIntervalRefresh && refreshInterval && config.isAutoRefreshEnabled\"\n [adaptivePosition]=\"false\"\n [container]=\"'body'\"\n [delay]=\"500\"\n >\n <input\n type=\"checkbox\"\n data-cy=\"c8y-alarms-widget--interval-toggle-button\"\n (click)=\"onToggleCountdownButtonState($event, WIDGET_TYPE_VALUES.ALARMS)\"\n />\n <c8y-countdown-interval\n *ngIf=\"isIntervalRefreshToggleOn\"\n [countdownInterval]=\"refreshInterval\"\n (countdownEnded)=\"countdownEnded()\"\n ></c8y-countdown-interval>\n <i\n c8yIcon=\"pause\"\n *ngIf=\"!isIntervalRefreshToggleOn\"\n ></i>\n </label>\n\n <div class=\"input-group-btn\">\n <button\n class=\"btn btn-default\"\n [attr.aria-label]=\"'Refresh' | translate\"\n [tooltip]=\"\n !isNewAlarmMessageCleared\n ? (REALTIME_UPDATE_ALARMS_MESSAGE | translate)\n : ('Refresh' | translate)\n \"\n placement=\"bottom\"\n type=\"button\"\n [adaptivePosition]=\"false\"\n [container]=\"'body'\"\n [delay]=\"500\"\n [disabled]=\"isRefreshDisabled || (isLoading | async)\"\n (click)=\"reload()\"\n data-cy=\"c8y-alarms-widget--reload-button\"\n >\n <span\n class=\"tag tag--info m-r-8\"\n *ngIf=\"!isNewAlarmMessageCleared\"\n >\n {{ 'New alarms' | translate }}\n </span>\n <i\n c8yIcon=\"refresh\"\n [ngClass]=\"{ 'icon-spin': isLoading | async }\"\n ></i>\n </button>\n </div>\n <div\n class=\"input-group-btn input-group-btn--last\"\n *ngIf=\"!isIntervalRefresh\"\n >\n <button\n class=\"c8y-realtime btn btn-default\"\n [attr.aria-label]=\"realtimeIconTitle\"\n [tooltip]=\"realtimeIconTitle\"\n placement=\"bottom\"\n type=\"button\"\n [container]=\"'body'\"\n (click)=\"toggleRealtimeState()\"\n [disabled]=\"isRefreshDisabled\"\n data-cy=\"c8y-alarms-widget--realtime-indicator\"\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 </div>\n </div>\n</div>" }]
}], ctorParameters: () => [{ type: i1.AlarmsViewService }, { type: i0.ChangeDetectorRef }, { type: i6.TranslateService }, { type: i2$1.WidgetGlobalAutoRefreshService }, { type: i2$1.GainsightService }], propDecorators: { isIntervalRefresh: [{
type: Input
}], refreshInterval: [{
type: Input
}], config: [{
type: Input
}], isLoading: [{
type: Input
}], isScrolling: [{
type: Input
}], isRefreshDisabled: [{
type: Input
}], onCountdownEnded: [{
type: Output
}], onRealTimeToggleChanged: [{
type: Output
}], countdownIntervalComponent: [{
type: ViewChild,
args: [CountdownIntervalComponent, { static: false }]
}] } });
/**
* To ensure backward compatibility, legacy widgets will be converted to the new implementation.
*/
class AlarmListWidgetComponent {
constructor(alarmWidgetService, alarmRealtimeService, alarmService, alarmsViewService, alarmWithChildrenRealtimeService, dashboardChild, alertService, widgetGlobalAutoRefresh) {
this.alarmWidgetService = alarmWidgetService;
this.alarmRealtimeService = alarmRealtimeService;
this.alarmService = alarmService;
this.alarmsViewService = alarmsViewService;
this.alarmWithChildrenRealtimeService = alarmWithChildrenRealtimeService;
this.dashboardChild = dashboardChild;
this.alertService = alertService;
this.widgetGlobalAutoRefresh = widgetGlobalAutoRefresh;
/**
* Represents current alarms.
*/
this.alarms$ = new BehaviorSubject(null);
/**
* Current isLoading state. Based on it next countdown cycle is being started.
*/
this.isLoading$ = new BehaviorSubject(true);
this.TIMEOUT_ERROR_TEXT = gettext('The request is taking longer than usual. We apologize for the inconvenience.');
this.SERVER_ERROR_TEXT = gettext('Server error occurred.');
this.unsubscribe$ = new Subject();
this.destroy$ = new Subject();
this.isDisabled = false;
this.isIntervalRefresh = this.alarmsViewService.isIntervalRefresh();
}
async ngOnInit() {
this.alerts.setAlertGroupDismissStrategy('warning', DismissAlertStrategy.TEMPORARY_OR_PERMANENT);
if (this.alarmWidgetService.isOldAlarmConfigStructure(this.config)) {
const mappedConfig = this.alarmWidgetService.mapToNewConfigStructure(this.config, this.isIntervalRefresh);
Object.assign(this.config, mappedConfig);
}
if (this.config.widgetInstanceGlobalAutoRefreshContext && !this.config.isRealtime) {
this.handleGlobalRefreshLoading();
}
const _config = this.dashboardChild['data']?.config;
const isLegacyWidgetWithoutConfiguration = isEmpty(_config) ||
isEqual(_config, { displaySettings: { globalAutoRefreshContext: true } }) ||
isEqual(_config, {
displaySettings: { globalAutoRefreshContext: true },
device: _config.device
});
if (isLegacyWidgetWithoutConfiguration) {
this.setLegacyRecentOrCriticalAlarmWidgetConfig();
}
this.updateWidgetRefreshMode();
this.refreshInterval = !!this.config?.refreshInterval && this.config?.refreshInterval;
await this.fetchAlarms();
}
ngAfterViewInit() {
if (!this.isIntervalRefresh && this.config.refreshOption === 'interval') {
this.handleRealtimeAlarms(this.config, this.alarmWidgetAlarmsReloadComp.isRealtimeToggleOn);
}
}
ngOnChanges(changes) {
if (changes?.config?.currentValue?.date) {
this.config.dateFilter = changes.config.currentValue.date;
}
}
ngOnDestroy() {
if (this.config.isRealtime) {
this.unsubscribe$.next();
this.unsubscribe$.complete();
}
this.destroy$.next();
this.destroy$.complete();
if (this.config.widgetInstanceGlobalAutoRefreshContext && this.isLoading$.value) {
this.widgetGlobalAutoRefresh.decrementLoading();
}
}
async fetchAlarms() {
this.updateDateFilter();
this.alarms$.next(await this.getAlarms());
}
async onDateFilterChange(event) {
this.config.dateFilter = event.selectedDates;
this.config.interval = event.interval;
await this.fetchAlarms();
}
handleRealTimeToggleChange(isRealtimeToggleOn) {
if (!isRealtimeToggleOn) {
this.unsubscribe$.next();
this.unsubscribe$.complete();
}
this.unsubscribe$ = new Subject();
this.handleRealtimeAlarms(this.config, isRealtimeToggleOn);
}
/**
* Updates the widget's date filter based on the currently selected interval.
* If the interval is set to last hour, we will configure the date filter to dynamically show alarms only from the last hour.
*/
updateDateFilter() {
if (this.config.interval === 'custom' || this.config.widgetInstanceGlobalTimeContext) {
return;
}
if (!this.config.interval) {
this.config.interval = 'none';
}
this.config.dateFilter = this.alarmsViewService.getDateTimeContextByInterval(this.config.interval);
}
/**
* Updates the widget's refresh mode based on the current configuration and the interval refresh setting.
* If the widget's 'isRealtime' configuration differs from the current global interval refresh setting (application options setting),
* the widget's mode is updated to either 'realtime' or 'interval'. In 'realtime' mode, the refresh interval is null,
* and in 'interval' mode, it is set to the default interval value.
*/
updateWidgetRefreshMode() {
const isRefreshTypeSwitched = !!this.config.isRealtime !== !this.isIntervalRefresh;
if (!isRefreshTypeSwitched) {
return;
}
this.config.isRealtime = !this.isIntervalRefresh;
if (this.config.isRealtime) {
// Switching widget to realtime mode
this.config.refreshInterval = null;
this.config.refreshOption = null;
this.config.widgetInstanceGlobalAutoRefreshContext = false;
this.config.isAutoRefreshEnabled = true;
}
else {
// Switching widget to interval mode
this.config.refreshInterval = this.alarmsViewService.DEFAULT_INTERVAL_VALUE;
this.config.refreshOption = this.alarmsViewService.DEFAULT_REFRESH_OPTION_VALUE;
this.config.widgetInstanceGlobalAutoRefreshContext = false;
}
}
async getAlarms() {
try {
this.isLoading$.next(true);
return await this.alarmService.list(this.alarmWidgetService.mapConfigToQueryFilter(this.config));
}
catch (error) {
this.alerts.addAlerts(new DynamicComponentAlert({
type: 'warning',
text: error?.name === 'TimeoutError'
? this.TIMEOUT_ERROR_TEXT
: (error?.message ?? this.SERVER_ERROR_TEXT)
}));
if (error?.res?.status === 403) {
this.isDisabled = true;
return;
}
this.alertService.addServerFailure(error);
}
finally {
this.isLoading$.next(false);
}
}
/**
* Handles the realtime updates of alarms based on the widget configuration.
* If a specific device is provided in the widget configuration, all alarms
* originating from that device and its children are filtered based on status, severity, and type.
* Otherwise, it processes all incoming alarms and filters them according to status, severity, and type,
* as specified in the widget configuration. If new alarms meet the specified criteria, it sets a flag
* to indicate the presence of new alarms.
*
* @param config - The configuration settings of the alarm list widget.
* @param isRealtimeToggleOn - A flag indicating whether the realtime toggle is on. If true, the alarm list will be refreshed.
* If false, the presence of new alarms will be displayed as "New Alarms" entry on widget refresh button, but the list won't be refreshed automatically.
*/
handleRealtimeAlarms(config, isRealtimeToggleOn) {
const callback = (data) => {
if (this.alarmWidgetService.filterAlarmsByStatusSeverityAndType(data, this.alarms$.value.data, config)) {
if (isRealtimeToggleOn) {
this.fetchAlarms();
this.alarmWidgetAlarmsReloadComp.isNewAlarmMessageCleared = true;
}
else {
this.alarmWidgetAlarmsReloadComp.isNewAlarmMessageCleared = false;
}
}
};
if (this.config.device) {
this.handleRealtime(this.alarmWithChildrenRealtimeService.onAll$.bind(this.alarmWithChildrenRealtimeService), callback);
}
else {
this.handleRealtime(this.alarmRealtimeService.onAll$.bind(this.alarmRealtimeService), callback);
}
}
/**
* Sets up a subscription to a realtime data stream. This function takes a subscriber function,
* which is used to subscribe to the data stream, and a callback function, which is called with each
* data update received from the stream.
*
* @param subscriberFn - A function that returns an observable stream of alarm data.
* @param callback - A function to be executed with each alarm data update.
*/
handleRealtime(subscriberFn, callback) {
subscriberFn(this.config.device?.id)
.pipe(tap(({ data }) => {
callback(data);
}), takeUntil(this.unsubscribe$))
.subscribe();
}
setLegacyRecentOrCriticalAlarmWidgetConfig() {
const widgetId = this.dashboardChild['data']?.componentId
? this.dashboardChild['data']?.componentId //property is used in cockpit dashboards
: this.dashboardChild['data']?.name; //property is used in device management dashboards
if (widgetId === RECENT_ALARMS_WIDGET_ID) {
const predefinedConfig = this.alarmWidgetService.getPredefinedConfiguration(this.isIntervalRefresh, RECENT_ALARMS_WIDGET_ID);
Object.assign(this.config, predefinedConfig);
return;
}
if (widgetId === ASSET_ALARMS_WIDGET_ID) {
const predefinedConfig = this.alarmWidgetService.getPredefinedConfiguration(this.isIntervalRefresh);
Object.assign(this.config, predefinedConfig);
}
}
handleGlobalRefreshLoading() {
this.isLoading$
.pipe(skip(1), globalAutoRefreshLoading(this.widgetGlobalAutoRefresh), takeUntil(this.destroy$))
.subscribe();
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AlarmListWidgetComponent, deps: [{ token: AlarmWidgetService }, { token: i2$1.AlarmRealtimeService }, { token: i2.AlarmService }, { token: i1.AlarmsViewService }, { token: i2$1.AlarmWithChildrenRealtimeService }, { token: i2$1.DashboardChildComponent }, { token: i2$1.AlertService }, { token: i2$1.WidgetGlobalAutoRefreshService }], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.14", type: AlarmListWidgetComponent, isStandalone: true, selector: "c8y-alarm-list-widget", inputs: { config: "config" }, providers: [AlarmRealtimeService, AlarmWithChildrenRealtimeService], viewQueries: [{ propertyName: "alarmWidgetAlarmsReloadComp", first: true, predicate: AlarmWidgetAlarmsReloadComponent, descendants: true }], usesOnChanges: true, ngImport: i0, template: "<c8y-alarms-list\n #list\n [alarms]=\"alarms$ | async\"\n [navigationOptions]=\"{\n alwaysNavigateToAllAlarms: !config.device,\n allowNavigationToAlarmsView: true,\n includeClearedQueryParams: true,\n queryParamsHandling: ''\n }\"\n [isInitialLoading]=\"isLoading$ | async\"\n [hasPermissions]=\"!isDisabled\"\n>\n <c8y-alarms-date-filter\n *ngIf=\"config.displayDateSelection\"\n [updateQueryParams]=\"false\"\n [date]=\"this.config.dateFilter\"\n [DEFAULT_INTERVAL]=\"config.interval || 'none'\"\n (dateFilterChange)=\"onDateFilterChange($event)\"\n ></c8y-alarms-date-filter>\n <c8y-alarm-widget-alarms-reload\n class=\"d-contents\"\n [isIntervalRefresh]=\"isIntervalRefresh\"\n [refreshInterval]=\"refreshInterval\"\n [config]=\"config\"\n [isLoading]=\"isLoading$\"\n [isScrolling]=\"list.isScrolling\"\n [isRefreshDisabled]=\"isDisabled\"\n (onCountdownEnded)=\"fetchAlarms()\"\n (onRealTimeToggleChanged)=\"handleRealTimeToggleChange($event)\"\n ></c8y-alarm-widget-alarms-reload>\n</c8y-alarms-list>\n", dependencies: [{ kind: "component", type: AlarmsListComponent, selector: "c8y-alarms-list", inputs: ["alarms", "hasPermissions", "typeFilters", "loadMoreMode", "navigationOptions", "isInitialLoading", "splitView"], outputs: ["onSelectedAlarm", "onScrollingStateChange"] }, { kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: AlarmsDateFilterComponent, selector: "c8y-alarms-date-filter", inputs: ["DEFAULT_INTERVAL", "updateQueryParams", "date"], outputs: ["dateFilterChange"] }, { kind: "component", type: AlarmWidgetAlarmsReloadComponent, selector: "c8y-alarm-widget-alarms-reload", inputs: ["isIntervalRefresh", "refreshInterval", "config", "isLoading", "isScrolling", "isRefreshDisabled"], outputs: ["onCountdownEnded", "onRealTimeToggleChanged"] }, { kind: "pipe", type: AsyncPipe, name: "async" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AlarmListWidgetComponent, decorators: [{
type: Component,
args: [{ selector: 'c8y-alarm-list-widget', providers: [AlarmRealtimeService, AlarmWithChildrenRealtimeService], imports: [
AlarmsListComponent,
NgIf,
AlarmsDateFilterComponent,
AlarmWidgetAlarmsReloadComponent,
AsyncPipe
], template: "<c8y-alarms-list\n #list\n [alarms]=\"alarms$ | async\"\n [navigationOptions]=\"{\n alwaysNavigateToAllAlarms: !config.device,\n allowNavigationToAlarmsView: true,\n includeClearedQueryParams: true,\n queryParamsHandling: ''\n }\"\n [isInitialLoading]=\"isLoading$ | async\"\n [hasPermissions]=\"!isDisabled\"\n>\n <c8y-alarms-date-filter\n *ngIf=\"config.displayDateSelection\"\n [updateQueryParams]=\"false\"\n [date]=\"this.config.dateFilter\"\n [DEFAULT_INTERVAL]=\"config.interval || 'none'\"\n (dateFilterChange)=\"onDateFilterChange($event)\"\n ></c8y-alarms-date-filter>\n <c8y-alarm-widget-alarms-reload\n class=\"d-contents\"\n [isIntervalRefresh]=\"isIntervalRefresh\"\n [refreshInterval]=\"refreshInterval\"\n [config]=\"config\"\n [isLoading]=\"isLoading$\"\n [isScrolling]=\"list.isScrolling\"\n [isRefreshDisabled]=\"isDisabled\"\n (onCountdownEnded)=\"fetchAlarms()\"\n (onRealTimeToggleChanged)=\"handleRealTimeToggleChange($event)\"\n ></c8y-alarm-widget-alarms-reload>\n</c8y-alarms-list>\n" }]
}], ctorParameters: () => [{ type: AlarmWidgetService }, { type: i2$1.AlarmRealtimeService }, { type: i2.AlarmService }, { type: i1.AlarmsViewService }, { type: i2$1.AlarmWithChildrenRealtimeService }, { type: i2$1.DashboardChildComponent }, { type: i2$1.AlertService }, { type: i2$1.WidgetGlobalAutoRefreshService }], propDecorators: { config: [{
type: Input
}], alarmWidgetAlarmsReloadComp: [{
type: ViewChild,
args: [AlarmWidgetAlarmsReloadComponent]
}] } });
class AlarmsWidgetModule {
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AlarmsWidgetModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.14", ngImport: i0, type: AlarmsWidgetModule, imports: [AlarmsModule,
CommonModule,
CountdownIntervalModule,
DocsModule,
FormsModule$1,
CommonModule$1,
FormsModule,
PopoverModule,
ReactiveFormsModule,
RouterModule, i1$1.TooltipModule, AlarmListWidgetComponent,
AlarmListWidgetConfigComponent,
AlarmWidgetAlarmsReloadComponent,
SeverityIconPipe,
SortingDescriptionPopoverMessagePipe] }); }
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AlarmsWidgetModule, imports: [AlarmsModule,
CommonModule,
CountdownIntervalModule,
DocsModule,
FormsModule$1,
CommonModule$1,
FormsModule,
PopoverModule,
ReactiveFormsModule,
RouterModule,
TooltipModule.forRoot(),
AlarmListWidgetComponent,
AlarmListWidgetConfigComponent,
AlarmWidgetAlarmsReloadComponent] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AlarmsWidgetModule, decorators: [{
type: NgModule,
args: [{
imports: [
AlarmsModule,
CommonModule,
CountdownIntervalModule,
DocsModule,
FormsModule$1,
CommonModule$1,
FormsModule,
PopoverModule,
ReactiveFormsModule,
RouterModule,
TooltipModule.forRoot(),
AlarmListWidgetComponent,
AlarmListWidgetConfigComponent,
AlarmWidgetAlarmsReloadComponent,
SeverityIconPipe,
SortingDescriptionPopoverMessagePipe
]
}]
}] });
/**
* Generated bundle index. Do not edit.
*/
export { ALARM_ORDER_LABELS, ALARM_ORDER_VALUES, ASSET_ALARMS_WIDGET_ID, AlarmListWidgetComponent, AlarmListWidgetConfigComponent, AlarmWidgetAlarmsReloadComponent, AlarmWidgetService, AlarmsWidgetModule, DATE_SELECTION, DEFAULT_PAGE_SIZE, GLOBAL_INTERVAL_OPTION, RECENT_ALARMS_WIDGET_ID, SeverityIconPipe, SortingDescriptionPopoverMessagePipe };
//# sourceMappingURL=c8y-ngx-components-widgets-implementations-alarms.mjs.map