UNPKG

@c8y/ngx-components

Version:

Angular modules for Cumulocity IoT applications

644 lines (639 loc) 67.9 kB
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