@c8y/ngx-components
Version:
Angular modules for Cumulocity IoT applications
644 lines (639 loc) • 67.9 kB
JavaScript
import { NgClass, AsyncPipe, CommonModule as CommonModule$1 } from '@angular/common';
import * as i0 from '@angular/core';
import { Injectable, Pipe, ViewChild, Input, Component, inject, signal, 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, AlarmService } from '@c8y/client';
import * as i4 from '@c8y/ngx-components';
import { FormGroupComponent, RequiredInputPlaceholderDirective, IconDirective, MessagesComponent, MessageDirective, C8yTranslatePipe, DashboardChildComponent, AlertService, DismissAlertStrategy, DynamicComponentAlert, 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 '@c8y/ngx-components/context-dashboard';
import { PRESET_NAME, REFRESH_OPTION, DateTimeContextUtil, LocalControlsComponent, WidgetConfigMigrationService, GLOBAL_CONTEXT_DISPLAY_MODE, CONTEXT_FEATURE, GlobalContextConnectorComponent, GlobalContextConfigComponent, GlobalContextInlineComponent, ConfigContextSelectorComponent, GlobalContextWidgetWrapperComponent } from '@c8y/ngx-components/global-context';
import { merge, isEmpty, isMatch } from 'lodash-es';
import { PopoverDirective, PopoverModule } from 'ngx-bootstrap/popover';
import { BehaviorSubject, Subject, EMPTY, from } from 'rxjs';
import { filter, tap, takeUntil, shareReplay, map, switchMap } from 'rxjs/operators';
import { gettext } from '@c8y/ngx-components/gettext';
import { defaultWidgetIds } from '@c8y/ngx-components/widgets/definitions';
import { RouterModule } from '@angular/router';
import * as i1$1 from 'ngx-bootstrap/tooltip';
import { TooltipModule } from 'ngx-bootstrap/tooltip';
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';
// TODO: remove
const GLOBAL_INTERVAL_OPTION = 'global-interval';
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(widgetId, existingConfig) {
// Check if any time/refresh configuration exists (cast to any for legacy properties)
const hasAnyTimeConfig = existingConfig?.dateTimeContext ||
existingConfig?.dateFilter ||
existingConfig?.date ||
existingConfig?.dateFrom ||
existingConfig?.dateTo ||
existingConfig?.refreshOption ||
existingConfig?.isAutoRefreshEnabled !== undefined ||
existingConfig?.refreshInterval ||
existingConfig?.aggregation !== undefined;
const config = {
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: [''],
widgetId: widgetId || ''
};
// Only add global context defaults if no time/refresh config exists at all
if (!hasAnyTimeConfig) {
config.isAutoRefreshEnabled = true;
config.refreshInterval = this.alarmsViewService.DEFAULT_INTERVAL_VALUE;
config.aggregation = null;
config.refreshOption = 'live';
config.dateTimeContext = {
dateFrom: new Date(0).toISOString(),
dateTo: new Date().toISOString(),
interval: 'custom'
};
}
return config;
}
/**
* 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) {
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: this.alarmsViewService.DEFAULT_INTERVAL_VALUE
};
}
/**
* 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.dateTimeContext) {
const { dateFrom, dateTo } = config.dateTimeContext;
filter.lastUpdatedFrom = typeof dateFrom === 'string' ? dateFrom : dateFrom.toISOString();
filter.createdTo = typeof dateTo === 'string' ? dateTo : dateTo.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: "20.3.19", ngImport: i0, type: AlarmWidgetService, deps: [{ token: i1.AlarmsViewService }], target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: AlarmWidgetService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", 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: "20.3.19", ngImport: i0, type: SeverityIconPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "20.3.19", ngImport: i0, type: SeverityIconPipe, isStandalone: true, name: "severityIcon" }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", 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: "20.3.19", ngImport: i0, type: SortingDescriptionPopoverMessagePipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "20.3.19", ngImport: i0, type: SortingDescriptionPopoverMessagePipe, isStandalone: true, name: "sortingDescriptionPopoverMessage" }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", 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, widgetConfigService) {
this.alarmListWidgetService = alarmListWidgetService;
this.alarmService = alarmService;
this.alarmsViewService = alarmsViewService;
this.alertService = alertService;
this.form = form;
this.formBuilder = formBuilder;
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.alarms$ = new BehaviorSubject(null);
this.orderList = Object.values(ALARM_ORDER_VALUES);
this.severityList = Object.keys(SEVERITY_LABELS);
/**
* Order does matter.
*/
this.statusList = [AlarmStatus.ACTIVE, AlarmStatus.ACKNOWLEDGED, AlarmStatus.CLEARED];
this.destroy$ = new Subject();
this.config$ = this.widgetConfigService.currentConfig$.pipe(filter(config => !!config), filter(c => !!c.dateTimeContext), tap(config => {
if (!this.formGroup) {
this.initializeForm();
}
else {
this.formGroup.patchValue(config, { emitEvent: false });
}
}), takeUntil(this.destroy$), shareReplay(1));
// Preview controls
this.controls = PRESET_NAME.ALARM_LIST;
}
ngOnInit() {
this.config$
.pipe(takeUntil(this.destroy$), filter(config => config?.isGlobalContextReady === true), map(config => {
if (config.refreshOption !== REFRESH_OPTION.HISTORY && config.dateTimeContext) {
return {
...config,
dateTimeContext: DateTimeContextUtil.normalizeForLive(config.dateTimeContext)
};
}
return config;
}), switchMap(config => {
if ((config?.severities && this.alarmListWidgetService.allValuesFalse(config.severities)) ||
(config?.status && this.alarmListWidgetService.allValuesFalse(config.status))) {
this.alarms$.next({ data: [], res: null });
return EMPTY;
}
const isWidgetWithExistingConfig = config.order && config.severities && config.status;
const filterConfig = isWidgetWithExistingConfig
? config
: { ...this.formGroup.value, ...config };
return from(this.getAlarms(filterConfig));
}))
.subscribe();
}
ngOnDestroy() {
this.destroy$.next();
this.destroy$.complete();
}
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;
}
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(''));
}
get types() {
return this.formGroup.get('types');
}
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, { emitEvent: false });
this.initializeTypes(this.config.types);
this.formGroup.valueChanges.pipe(takeUntil(this.destroy$)).subscribe(value => {
this.config = merge(this.config, value);
this.getAlarms(this.config);
});
}
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,
device: this.config.device ? new FormControl(this.config.device) : new FormControl(undefined)
});
}
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(''));
}
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: AlarmListWidgetConfigComponent, deps: [{ token: AlarmWidgetService }, { token: i2.AlarmService }, { token: i1.AlarmsViewService }, { token: i4.AlertService }, { token: i5.NgForm }, { token: i5.FormBuilder }, { token: i6.WidgetConfigService }], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.19", type: AlarmListWidgetConfigComponent, isStandalone: true, selector: "c8y-alarm-list-widget-config", inputs: { config: "config" }, viewQueries: [{ propertyName: "previewMapSet", first: true, predicate: ["alarmListPreview"], descendants: 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@if (formGroup) {\n <form\n class=\"row d-flex flex-wrap\"\n [formGroup]=\"formGroup\"\n >\n <div class=\"col-md-6\">\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 @for (order of orderList; track order; let i = $index) {\n <div\n class=\"d-flex p-b-8 p-t-4 a-i-center\"\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 }\n </c8y-form-group>\n </fieldset>\n </div>\n <div class=\"col-md-6 col-xs-12\">\n <fieldset\n class=\"c8y-fieldset\"\n formGroupName=\"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 @for (severityOption of severityList; track severityOption) {\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 @let severityIcon = severityOption | severityIcon;\n <i\n class=\"a-s-center m-r-4 icon-20 {{ severityIcon.iconClass }}\"\n [c8yIcon]=\"severityIcon.c8yIcon\"\n ></i>\n <span>{{ SEVERITY_LABELS[severityOption] | translate }}</span>\n </label>\n }\n <c8y-messages>\n @if (formGroup.controls.severities.hasError('required')) {\n <c8y-message>\n {{ 'Select at least one severity.' | translate }}\n </c8y-message>\n }\n </c8y-messages>\n </c8y-form-group>\n </fieldset>\n @if (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 }\n </div>\n\n <div class=\"col-md-6 col-xs-12\">\n <fieldset\n class=\"c8y-fieldset\"\n formGroupName=\"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 @for (statusOption of statusList; track statusOption) {\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 }\n <c8y-messages>\n @if (formGroup.controls.status.hasError('required')) {\n <c8y-message>\n {{ 'Select at least one status.' | translate }}\n </c8y-message>\n }\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 @for (type of types.controls; track type; let i = $index) {\n <div\n class=\"input-group m-b-4\"\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 @if (i === types.controls.length - 1) {\n <div class=\"input-group-btn\">\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 }\n </div>\n }\n </c8y-form-group>\n </fieldset>\n </div>\n </form>\n}\n\n<ng-template #alarmListPreview>\n @if ((config$ | async)?.displayMode !== 'dashboard') {\n <c8y-local-controls\n [controls]=\"controls\"\n [displayMode]=\"(config$ | async)?.displayMode\"\n [config]=\"config$ | async\"\n [disabled]=\"true\"\n ></c8y-local-controls>\n }\n <c8y-alarms-list\n [alarms]=\"alarms$ | async\"\n [isInPreviewMode]=\"true\"\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.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.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: "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.FormGroupName, selector: "[formGroupName]", inputs: ["formGroupName"] }, { kind: "directive", type: i5.FormArrayName, selector: "[formArrayName]", inputs: ["formArrayName"] }, { 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: "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: 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", "additionalMessages"] }, { 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", "isInPreviewMode"], outputs: ["onSelectedAlarm", "onScrollingStateChange"] }, { kind: "component", type: LocalControlsComponent, selector: "c8y-local-controls", inputs: ["controls", "displayMode", "config", "isLoading", "disabled", "emitRefresh"], outputs: ["configChange", "refresh"] }, { 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: "20.3.19", ngImport: i0, type: AlarmListWidgetConfigComponent, decorators: [{
type: Component,
args: [{ selector: 'c8y-alarm-list-widget-config', viewProviders: [{ provide: ControlContainer, useExisting: NgForm }], imports: [
FormsModule,
ReactiveFormsModule,
PopoverDirective,
FormGroupComponent,
AlarmsDateFilterComponent,
RequiredInputPlaceholderDirective,
NgClass,
IconDirective,
MessagesComponent,
MessageDirective,
AlarmsListComponent,
C8yTranslatePipe,
AsyncPipe,
SeverityIconPipe,
SortingDescriptionPopoverMessagePipe,
LocalControlsComponent
], template: "<div\n class=\"p-l-24 p-r-24\"\n [style.pointer-events]=\"'auto'\"\n [style.opacity]=\"1\"\n></div>\n\n@if (formGroup) {\n <form\n class=\"row d-flex flex-wrap\"\n [formGroup]=\"formGroup\"\n >\n <div class=\"col-md-6\">\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 @for (order of orderList; track order; let i = $index) {\n <div\n class=\"d-flex p-b-8 p-t-4 a-i-center\"\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 }\n </c8y-form-group>\n </fieldset>\n </div>\n <div class=\"col-md-6 col-xs-12\">\n <fieldset\n class=\"c8y-fieldset\"\n formGroupName=\"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 @for (severityOption of severityList; track severityOption) {\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 @let severityIcon = severityOption | severityIcon;\n <i\n class=\"a-s-center m-r-4 icon-20 {{ severityIcon.iconClass }}\"\n [c8yIcon]=\"severityIcon.c8yIcon\"\n ></i>\n <span>{{ SEVERITY_LABELS[severityOption] | translate }}</span>\n </label>\n }\n <c8y-messages>\n @if (formGroup.controls.severities.hasError('required')) {\n <c8y-message>\n {{ 'Select at least one severity.' | translate }}\n </c8y-message>\n }\n </c8y-messages>\n </c8y-form-group>\n </fieldset>\n @if (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 }\n </div>\n\n <div class=\"col-md-6 col-xs-12\">\n <fieldset\n class=\"c8y-fieldset\"\n formGroupName=\"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 @for (statusOption of statusList; track statusOption) {\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 }\n <c8y-messages>\n @if (formGroup.controls.status.hasError('required')) {\n <c8y-message>\n {{ 'Select at least one status.' | translate }}\n </c8y-message>\n }\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 fr