@c8y/ngx-components
Version:
Angular modules for Cumulocity IoT applications
567 lines (559 loc) • 94.3 kB
JavaScript
import * as i0 from '@angular/core';
import { Injectable, forwardRef, Input, Component, EventEmitter, Output, Pipe, ContentChild, Optional, NgModule } from '@angular/core';
import * as i1 from '@angular/forms';
import { FormControl, Validators, NG_VALUE_ACCESSOR, NG_VALIDATORS } from '@angular/forms';
import { take, takeUntil, map, startWith, tap, switchMap, shareReplay, distinctUntilChanged, debounceTime } from 'rxjs/operators';
import * as i3 from '@angular/common';
import * as i2 from '@c8y/ngx-components';
import { gettext, ViewContext, EmptyStateComponent, CommonModule, CoreModule } from '@c8y/ngx-components';
import * as i8 from '@angular/cdk/drag-drop';
import { moveItemInArray, DragDropModule } from '@angular/cdk/drag-drop';
import * as i6$1 from '@c8y/ngx-components/context-dashboard';
import * as i1$2 from 'ngx-bootstrap/modal';
import { Subject, BehaviorSubject, combineLatest } from 'rxjs';
import * as i1$1 from '@c8y/client';
import { uniqBy } from 'lodash-es';
import * as i5 from '@c8y/ngx-components/assets-navigator';
import { AssetSelectorModule } from '@c8y/ngx-components/assets-navigator';
import * as i6 from 'ngx-bootstrap/collapse';
import { CollapseModule } from 'ngx-bootstrap/collapse';
import * as i4 from 'ngx-bootstrap/popover';
import { PopoverModule } from 'ngx-bootstrap/popover';
import * as i4$1 from '@angular/router';
import { RouterModule } from '@angular/router';
import { TooltipModule } from 'ngx-bootstrap/tooltip';
class AlarmEventAttributesFormService {
duplicateTypeValidator(selectedItems) {
return (control) => {
if (!control.value || !selectedItems?.length) {
return null;
}
const isDuplicate = selectedItems.some(item => item.filters?.type?.toLowerCase() === control.value.toLowerCase());
return isDuplicate ? { duplicateType: true } : null;
};
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AlarmEventAttributesFormService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AlarmEventAttributesFormService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AlarmEventAttributesFormService, decorators: [{
type: Injectable,
args: [{ providedIn: 'root' }]
}] });
class AlarmEventAttributesFormComponent {
constructor(formBuilder, alarmEventAttributesFormService) {
this.formBuilder = formBuilder;
this.alarmEventAttributesFormService = alarmEventAttributesFormService;
this.omitProperties = {};
this.selectedItems = [];
this.datapoints = [];
}
ngOnInit() {
this.formGroup = this.formBuilder.group({
filters: this.formBuilder.group({
type: new FormControl('', [
Validators.required,
this.alarmEventAttributesFormService.duplicateTypeValidator(this.selectedItems)
])
}),
timelineType: new FormControl(''),
selectedDatapoint: new FormControl(null)
});
if (!this.omitProperties.label) {
this.formGroup.addControl('label', new FormControl('', Validators.required));
}
if (this.datapoints?.length > 0) {
this.filterKpis();
}
}
validate(_control) {
return this.formGroup?.valid ? null : { formInvalid: {} };
}
writeValue(obj) {
this.formGroup.patchValue(obj);
}
registerOnChange(fn) {
this.formGroup.valueChanges.subscribe(fn);
}
registerOnTouched(fn) {
this.formGroup.valueChanges.pipe(take(1)).subscribe(fn);
}
setDisabledState(isDisabled) {
isDisabled ? this.formGroup.disable() : this.formGroup.enable();
}
changeDatapointSelection(event) {
const selectElement = event.target;
const selectedIndex = selectElement.selectedIndex;
const selectedDatapoint = this.datapoints[selectedIndex];
const trimmedDatapoint = {
target: selectedDatapoint.__target.id,
fragment: selectedDatapoint.fragment,
series: selectedDatapoint.series
};
this.formGroup.patchValue({ selectedDatapoint: trimmedDatapoint });
}
trackByFn(_index, item) {
return `${item.fragment}-${item.series}-${item.__target?.id}`;
}
filterKpis() {
if (this.target && this.target.id) {
this.datapoints = this.datapoints.filter(dp => dp.__target?.id === this.target.id);
}
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AlarmEventAttributesFormComponent, deps: [{ token: i1.FormBuilder }, { token: AlarmEventAttributesFormService }], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.14", type: AlarmEventAttributesFormComponent, isStandalone: false, selector: "c8y-alarm-event-attributes-form", inputs: { timelineType: "timelineType", omitProperties: "omitProperties", selectedItems: "selectedItems", target: "target", datapoints: "datapoints" }, providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => AlarmEventAttributesFormComponent),
multi: true
},
{
provide: NG_VALIDATORS,
useExisting: forwardRef(() => AlarmEventAttributesFormComponent),
multi: true
}
], ngImport: i0, template: "<div [formGroup]=\"formGroup\">\n <c8y-form-group\n class=\"form-group-sm\"\n *ngIf=\"formGroup.controls.label\"\n >\n <label class=\"m-0\">{{ 'Label' | translate }}</label>\n <input\n class=\"form-control flex-grow\"\n name=\"label\"\n formControlName=\"label\"\n [placeholder]=\"\n 'e.g. {{ example }}'\n | translate\n : {\n example: timelineType === 'ALARM' ? 'Alarm unavailable' : 'Location update'\n }\n \"\n />\n </c8y-form-group>\n\n <div formGroupName=\"filters\">\n <c8y-form-group class=\"form-group-sm\">\n <label class=\"m-0\">{{ 'Type' | translate }}</label>\n <input\n class=\"form-control flex-grow\"\n name=\"type\"\n formControlName=\"type\"\n [placeholder]=\"\n 'e.g. {{ example }}'\n | translate\n : {\n example:\n timelineType === 'ALARM' ? 'c8y_UnavailabilityAlarm' : 'c8y_LocationUpdate'\n }\n \"\n />\n <c8y-messages>\n <c8y-message\n name=\"duplicateType\"\n [text]=\"'Type already used.' | translate\"\n ></c8y-message>\n </c8y-messages>\n </c8y-form-group>\n </div>\n\n <c8y-form-group\n class=\"form-group-sm\"\n *ngIf=\"datapoints?.length > 0\"\n >\n <label>{{ 'Select data point' | translate }}</label>\n <div class=\"c8y-select-wrapper\">\n <select\n class=\"form-control input-sm\"\n (change)=\"changeDatapointSelection($event)\"\n >\n <option\n title=\"{{ 'Widget configuration' | translate }}\"\n *ngFor=\"let datapoint of datapoints; trackBy: trackByFn\"\n [ngValue]=\"{\n target: datapoint.__target.id,\n fragment: datapoint.fragment,\n series: datapoint.series\n }\"\n [selected]=\"\n datapoint.__target.id === formGroup.value.selectedDatapoint?.target &&\n datapoint.fragment === formGroup.value.selectedDatapoint?.fragment &&\n datapoint.series === formGroup.value.selectedDatapoint?.series\n \"\n >\n {{ datapoint?.label }}\n </option>\n </select>\n </div>\n </c8y-form-group>\n</div>\n", dependencies: [{ kind: "directive", type: i3.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i3.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "component", type: i2.FormGroupComponent, selector: "c8y-form-group", inputs: ["hasError", "hasWarning", "hasSuccess", "novalidation", "status"] }, { kind: "directive", type: i2.MessageDirective, selector: "c8y-message", inputs: ["name", "text"] }, { kind: "component", type: i2.MessagesComponent, selector: "c8y-messages", inputs: ["show", "defaults", "helpMessage"] }, { kind: "directive", type: i2.RequiredInputPlaceholderDirective, selector: "input[required], input[formControlName]" }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "directive", type: i1.FormGroupName, selector: "[formGroupName]", inputs: ["formGroupName"] }, { kind: "pipe", type: i2.C8yTranslatePipe, name: "translate" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AlarmEventAttributesFormComponent, decorators: [{
type: Component,
args: [{ selector: 'c8y-alarm-event-attributes-form', providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => AlarmEventAttributesFormComponent),
multi: true
},
{
provide: NG_VALIDATORS,
useExisting: forwardRef(() => AlarmEventAttributesFormComponent),
multi: true
}
], standalone: false, template: "<div [formGroup]=\"formGroup\">\n <c8y-form-group\n class=\"form-group-sm\"\n *ngIf=\"formGroup.controls.label\"\n >\n <label class=\"m-0\">{{ 'Label' | translate }}</label>\n <input\n class=\"form-control flex-grow\"\n name=\"label\"\n formControlName=\"label\"\n [placeholder]=\"\n 'e.g. {{ example }}'\n | translate\n : {\n example: timelineType === 'ALARM' ? 'Alarm unavailable' : 'Location update'\n }\n \"\n />\n </c8y-form-group>\n\n <div formGroupName=\"filters\">\n <c8y-form-group class=\"form-group-sm\">\n <label class=\"m-0\">{{ 'Type' | translate }}</label>\n <input\n class=\"form-control flex-grow\"\n name=\"type\"\n formControlName=\"type\"\n [placeholder]=\"\n 'e.g. {{ example }}'\n | translate\n : {\n example:\n timelineType === 'ALARM' ? 'c8y_UnavailabilityAlarm' : 'c8y_LocationUpdate'\n }\n \"\n />\n <c8y-messages>\n <c8y-message\n name=\"duplicateType\"\n [text]=\"'Type already used.' | translate\"\n ></c8y-message>\n </c8y-messages>\n </c8y-form-group>\n </div>\n\n <c8y-form-group\n class=\"form-group-sm\"\n *ngIf=\"datapoints?.length > 0\"\n >\n <label>{{ 'Select data point' | translate }}</label>\n <div class=\"c8y-select-wrapper\">\n <select\n class=\"form-control input-sm\"\n (change)=\"changeDatapointSelection($event)\"\n >\n <option\n title=\"{{ 'Widget configuration' | translate }}\"\n *ngFor=\"let datapoint of datapoints; trackBy: trackByFn\"\n [ngValue]=\"{\n target: datapoint.__target.id,\n fragment: datapoint.fragment,\n series: datapoint.series\n }\"\n [selected]=\"\n datapoint.__target.id === formGroup.value.selectedDatapoint?.target &&\n datapoint.fragment === formGroup.value.selectedDatapoint?.fragment &&\n datapoint.series === formGroup.value.selectedDatapoint?.series\n \"\n >\n {{ datapoint?.label }}\n </option>\n </select>\n </div>\n </c8y-form-group>\n</div>\n" }]
}], ctorParameters: () => [{ type: i1.FormBuilder }, { type: AlarmEventAttributesFormService }], propDecorators: { timelineType: [{
type: Input
}], omitProperties: [{
type: Input
}], selectedItems: [{
type: Input
}], target: [{
type: Input
}], datapoints: [{
type: Input
}] } });
const EVENT_TEXTS = {
listTitle: gettext('Events'),
emptyStateIcon: 'online1',
emptyStateTitle: gettext('No events to display.'),
emptyStateSubtitle: gettext('Add your first event.'),
addButtonLabel: gettext('Add event'),
addCustomItemButtonLabel: gettext('Add custom event'),
selectorTitle: gettext('Events selector'),
availableItemsTitle: gettext('Available events'),
assetWithNoItemsEmptyStateSubtitle: gettext('Select an asset with events from the list.'),
largeNumberOfItemsInfo: gettext('Due to the large number, only a subset of events are displayed. Use search to narrow down the number of results.'),
selectedItemsTitle: gettext('Selected events'),
noSelectedItemsTitle: gettext('No events selected.'),
recentItemsWarningTitle: gettext('The list below may not be complete.'),
recentItemsWarningText: gettext('Recent events are displayed below. Past events might not be shown.'),
addCustomText: gettext('Optionally you can add a custom event.')
};
const ALARM_TEXTS = {
listTitle: gettext('Alarms'),
emptyStateIcon: 'c8y-alarm',
emptyStateTitle: gettext('No alarms to display.'),
emptyStateSubtitle: gettext('Add your first alarm.'),
addButtonLabel: gettext('Add alarm'),
addCustomItemButtonLabel: gettext('Add custom alarm'),
selectorTitle: gettext('Alarms selector'),
availableItemsTitle: gettext('Available alarms'),
assetWithNoItemsEmptyStateSubtitle: gettext('Select an asset with alarms from the list.'),
largeNumberOfItemsInfo: gettext('Due to the large number, only a subset of alarms are displayed. Use search to narrow down the number of results.'),
selectedItemsTitle: gettext('Selected alarms'),
noSelectedItemsTitle: gettext('No alarms selected.'),
recentItemsWarningTitle: gettext('The list below may not be complete.'),
recentItemsWarningText: gettext('Recent alarms are displayed below. Past alarms might not be shown.'),
addCustomText: gettext('Optionally you can add a custom alarm.')
};
class AlarmEventSelectorService {
constructor(alarmService, eventsService, color) {
this.alarmService = alarmService;
this.eventsService = eventsService;
this.color = color;
this.timelineTypeTextsMap = new Map([
['ALARM', ALARM_TEXTS],
['EVENT', EVENT_TEXTS]
]);
}
/**
* This method returns the texts for the timeline type.
* @param timelineType The timeline type.
* @returns The texts for the timeline type.
*/
timelineTypeTexts(timelineType) {
return this.timelineTypeTextsMap.get(timelineType);
}
/**
* This method returns all alarms or events of the platform based on the timeline type
* @param timelineType The timeline type.
* @returns The alarms or events of the asset.
*/
async getItems(timelineType) {
const filters = {
pageSize: 1000
};
return timelineType === 'ALARM'
? await this.getAlarmsOfAsset(filters)
: await this.getEventsOfAsset(filters);
}
/**
* This method returns the items of the asset based on the timeline type.
* @param parentReference The parent reference.
* @param timelineType The timeline type.
* @returns The alarms or events of the asset.
*/
async getAlarmsOrEvents(parentReference, timelineType) {
const filters = { source: parentReference.id, pageSize: 1000 };
return timelineType === 'ALARM'
? await this.getAlarmsOfAsset(filters)
: await this.getEventsOfAsset(filters);
}
async getUniqueAlarmsOnly(data) {
return Promise.all(uniqBy(data, 'type').map(async (alarm) => this.createItem('ALARM', alarm)));
}
async getUniqueEventsOnly(data) {
return Promise.all(uniqBy(data, 'type').map(async (event) => this.createItem('EVENT', event)));
}
async createItem(timelineType, item) {
const color = await this.color.generateColor(item.type);
if (item.source && !item.source.name) {
item.source.name = `Device ${item.source.id}`;
}
return {
timelineType,
color,
label: item.type,
filters: {
type: item.type
},
__target: item.source
};
}
async getAlarmsOfAsset(filters) {
const res = await this.alarmService.list({
...filters,
withSourceAssets: !!filters.source,
withSourceDevices: !!filters.source,
pageSize: 1000
});
const alarms = await this.getUniqueAlarmsOnly(res.data);
return alarms;
}
async getEventsOfAsset(filters) {
const res = await this.eventsService.list(filters);
const events = this.getUniqueEventsOnly(res.data);
return events;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AlarmEventSelectorService, deps: [{ token: i1$1.AlarmService }, { token: i1$1.EventService }, { token: i2.ColorService }], target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AlarmEventSelectorService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AlarmEventSelectorService, decorators: [{
type: Injectable,
args: [{ providedIn: 'root' }]
}], ctorParameters: () => [{ type: i1$1.AlarmService }, { type: i1$1.EventService }, { type: i2.ColorService }] });
class AlarmEventSelectorListItemComponent {
constructor(formBuilder) {
this.formBuilder = formBuilder;
this.showAddRemoveButton = true;
this.isSelected = false;
this.optionToRemove = false;
this.showActiveToggle = false;
this.allowItemEdit = false;
this.hideSource = false;
this.displayAsSwitch = false;
this.omitProperties = {};
this.added = new EventEmitter();
this.removed = new EventEmitter();
this.destroy$ = new Subject();
this.formGroup = this.formBuilder.group({
details: [],
color: [],
__active: [],
__target: []
});
this.valid$ = this.formGroup.statusChanges.pipe(takeUntil(this.destroy$), map(val => val === 'VALID'));
}
ngOnInit() {
this.colorPickerTitle = this.allowItemEdit ? gettext('Change color') : '';
}
ngOnDestroy() {
this.destroy$.next();
this.destroy$.complete();
}
toggleActive() {
this.formGroup.controls.__active.setValue(!this.formGroup.value.__active);
}
validate(_control) {
return this.formGroup?.valid ? null : { formInvalid: {} };
}
writeValue(obj) {
this.formGroup.patchValue({ ...obj, details: obj });
}
registerOnChange(fn) {
this.formGroup.valueChanges
.pipe(map(tmp => this.transformFormValue(tmp)), takeUntil(this.destroy$))
.subscribe(fn);
}
registerOnTouched(fn) {
this.formGroup.valueChanges.pipe(take(1)).subscribe(fn);
}
setDisabledState(isDisabled) {
isDisabled ? this.formGroup.disable() : this.formGroup.enable();
}
addOrRemoveItem() {
const value = this.transformFormValue(this.formGroup.value);
if (this.isSelected) {
this.removed.emit(value);
}
else {
this.added.emit(value);
}
}
remove() {
this.removed.emit(this.transformFormValue(this.formGroup.value));
}
transformFormValue(formValue) {
const obj = Object.assign({}, formValue.details || {}, formValue);
delete obj.details;
return obj;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AlarmEventSelectorListItemComponent, deps: [{ token: i1.FormBuilder }], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.14", type: AlarmEventSelectorListItemComponent, isStandalone: false, selector: "c8y-alarm-event-selector-list-item", inputs: { timelineType: "timelineType", datapoints: "datapoints", highlightText: "highlightText", showAddRemoveButton: "showAddRemoveButton", isSelected: "isSelected", optionToRemove: "optionToRemove", showActiveToggle: "showActiveToggle", allowItemEdit: "allowItemEdit", hideSource: "hideSource", displayAsSwitch: "displayAsSwitch", omitProperties: "omitProperties" }, outputs: { added: "added", removed: "removed" }, providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => AlarmEventSelectorListItemComponent),
multi: true
},
{
provide: NG_VALIDATORS,
useExisting: forwardRef(() => AlarmEventSelectorListItemComponent),
multi: true
}
], ngImport: i0, template: "<c8y-li\n class=\"c8y-list__item__collapse--container-small\"\n [formGroup]=\"formGroup\"\n #li\n>\n <c8y-li-drag-handle><ng-content select=\"c8y-li-drag-handle\"></ng-content></c8y-li-drag-handle>\n <c8y-li-icon\n class=\"a-s-center p-r-4\"\n *ngIf=\"showAddRemoveButton\"\n >\n <button\n class=\"btn btn-clean text-primary\"\n [title]=\"'Remove' | translate\"\n *ngIf=\"isSelected\"\n (click)=\"addOrRemoveItem()\"\n >\n <i\n class=\"text-danger\"\n c8yIcon=\"minus-circle\"\n ></i>\n </button>\n <button\n class=\"btn btn-clean text-primary\"\n [title]=\"'Select' | translate\"\n *ngIf=\"!isSelected\"\n (click)=\"addOrRemoveItem()\"\n [disabled]=\"(valid$ | async) === false\"\n >\n <i\n class=\"text-primary\"\n c8yIcon=\"plus-circle\"\n ></i>\n </button>\n </c8y-li-icon>\n\n <c8y-li-checkbox\n class=\"a-s-center m-t-4 p-r-0\"\n *ngIf=\"showActiveToggle\"\n [displayAsSwitch]=\"displayAsSwitch\"\n formControlName=\"__active\"\n (click)=\"$event.stopPropagation()\"\n ></c8y-li-checkbox>\n\n <div class=\"d-flex a-i-center p-l-0\">\n <div\n class=\"c8y-list__item__colorpicker p-t-0 p-b-0 p-l-4\"\n [title]=\"colorPickerTitle | translate\"\n >\n <div\n class=\"c8y-colorpicker\"\n [ngClass]=\"{\n 'c8y-colorpicker--alarm': timelineType === 'ALARM',\n 'c8y-colorpicker--event': timelineType === 'EVENT'\n }\"\n >\n <input\n [ngStyle]=\"{ 'pointer-events': allowItemEdit && !omitProperties.color ? 'auto' : 'none' }\"\n type=\"color\"\n formControlName=\"color\"\n (click)=\"$event.stopPropagation()\"\n />\n <span\n class=\"circle-icon-wrapper circle-icon-wrapper--medium\"\n [ngStyle]=\"{ 'background-color': formGroup.value.color }\"\n >\n <i\n class=\"stroked-icon\"\n [c8yIcon]=\"timelineType === 'EVENT' ? 'online1' : 'bell'\"\n ></i>\n </span>\n </div>\n </div>\n <button\n class=\"btn-clean text-truncate\"\n [title]=\"formGroup.value.details?.label\"\n (click)=\"\n allowItemEdit\n ? (li.collapsed = !li.collapsed)\n : showActiveToggle\n ? toggleActive()\n : addOrRemoveItem()\n \"\n >\n <span class=\"text-truncate\">\n <c8y-highlight\n [text]=\"\n omitProperties.label\n ? formGroup.value.details?.filters?.type\n : formGroup.value.details?.label\n \"\n [pattern]=\"highlightText\"\n [shouldTrimPattern]=\"true\"\n ></c8y-highlight>\n </span>\n <small\n class=\"text-truncate text-muted icon-flex\"\n *ngIf=\"formGroup.value.__target && !hideSource\"\n >\n <i c8yIcon=\"exchange\"></i>\n <span class=\"text-truncate\">{{ formGroup.value.__target.name }}</span>\n </small>\n </button>\n\n <button\n class=\"btn-dot btn-dot--danger m-l-auto\"\n title=\"{{ 'Invalid entries' | translate }}\"\n [popover]=\"'Some entries are invalid. Check the required input fields.' | translate\"\n container=\"body\"\n *ngIf=\"(valid$ | async) === false && li.collapsed\"\n [outsideClick]=\"true\"\n >\n <i c8yIcon=\"exclamation-circle\"></i>\n </button>\n </div>\n\n <c8y-li-action\n [icon]=\"'minus-circle'\"\n *ngIf=\"optionToRemove\"\n [label]=\"'Remove from list' | translate\"\n (click)=\"remove()\"\n ></c8y-li-action>\n <c8y-li-collapse *ngIf=\"allowItemEdit\">\n <div class=\"data-point-details\">\n <c8y-alarm-event-attributes-form\n formControlName=\"details\"\n [timelineType]=\"timelineType\"\n [omitProperties]=\"omitProperties\"\n [datapoints]=\"datapoints\"\n [target]=\"formGroup.value.__target\"\n ></c8y-alarm-event-attributes-form>\n </div>\n </c8y-li-collapse>\n</c8y-li>\n", dependencies: [{ kind: "directive", type: i2.IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "directive", type: i3.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i3.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i3.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "component", type: i2.HighlightComponent, selector: "c8y-highlight", inputs: ["pattern", "text", "elementClass", "shouldTrimPattern"] }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2.RequiredInputPlaceholderDirective, selector: "input[required], input[formControlName]" }, { kind: "component", type: i2.ListItemComponent, selector: "c8y-list-item, c8y-li", inputs: ["active", "highlighted", "emptyActions", "dense", "collapsed", "selectable"], outputs: ["collapsedChange"] }, { kind: "component", type: i2.ListItemIconComponent, selector: "c8y-list-item-icon, c8y-li-icon", inputs: ["icon", "status"] }, { kind: "component", type: i2.ListItemActionComponent, selector: "c8y-list-item-action, c8y-li-action", inputs: ["label", "icon", "disabled"], outputs: ["click"] }, { kind: "component", type: i2.ListItemCollapseComponent, selector: "c8y-list-item-collapse, c8y-li-collapse", inputs: ["collapseWay"] }, { kind: "component", type: i2.ListItemCheckboxComponent, selector: "c8y-list-item-checkbox, c8y-li-checkbox", inputs: ["selected", "indeterminate", "disabled", "displayAsSwitch"], outputs: ["onSelect"] }, { kind: "component", type: i2.ListItemDragHandleComponent, selector: "c8y-list-item-drag-handle, c8y-li-drag-handle" }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "directive", type: i4.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: AlarmEventAttributesFormComponent, selector: "c8y-alarm-event-attributes-form", inputs: ["timelineType", "omitProperties", "selectedItems", "target", "datapoints"] }, { kind: "pipe", type: i2.C8yTranslatePipe, name: "translate" }, { kind: "pipe", type: i3.AsyncPipe, name: "async" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AlarmEventSelectorListItemComponent, decorators: [{
type: Component,
args: [{ selector: 'c8y-alarm-event-selector-list-item', providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => AlarmEventSelectorListItemComponent),
multi: true
},
{
provide: NG_VALIDATORS,
useExisting: forwardRef(() => AlarmEventSelectorListItemComponent),
multi: true
}
], standalone: false, template: "<c8y-li\n class=\"c8y-list__item__collapse--container-small\"\n [formGroup]=\"formGroup\"\n #li\n>\n <c8y-li-drag-handle><ng-content select=\"c8y-li-drag-handle\"></ng-content></c8y-li-drag-handle>\n <c8y-li-icon\n class=\"a-s-center p-r-4\"\n *ngIf=\"showAddRemoveButton\"\n >\n <button\n class=\"btn btn-clean text-primary\"\n [title]=\"'Remove' | translate\"\n *ngIf=\"isSelected\"\n (click)=\"addOrRemoveItem()\"\n >\n <i\n class=\"text-danger\"\n c8yIcon=\"minus-circle\"\n ></i>\n </button>\n <button\n class=\"btn btn-clean text-primary\"\n [title]=\"'Select' | translate\"\n *ngIf=\"!isSelected\"\n (click)=\"addOrRemoveItem()\"\n [disabled]=\"(valid$ | async) === false\"\n >\n <i\n class=\"text-primary\"\n c8yIcon=\"plus-circle\"\n ></i>\n </button>\n </c8y-li-icon>\n\n <c8y-li-checkbox\n class=\"a-s-center m-t-4 p-r-0\"\n *ngIf=\"showActiveToggle\"\n [displayAsSwitch]=\"displayAsSwitch\"\n formControlName=\"__active\"\n (click)=\"$event.stopPropagation()\"\n ></c8y-li-checkbox>\n\n <div class=\"d-flex a-i-center p-l-0\">\n <div\n class=\"c8y-list__item__colorpicker p-t-0 p-b-0 p-l-4\"\n [title]=\"colorPickerTitle | translate\"\n >\n <div\n class=\"c8y-colorpicker\"\n [ngClass]=\"{\n 'c8y-colorpicker--alarm': timelineType === 'ALARM',\n 'c8y-colorpicker--event': timelineType === 'EVENT'\n }\"\n >\n <input\n [ngStyle]=\"{ 'pointer-events': allowItemEdit && !omitProperties.color ? 'auto' : 'none' }\"\n type=\"color\"\n formControlName=\"color\"\n (click)=\"$event.stopPropagation()\"\n />\n <span\n class=\"circle-icon-wrapper circle-icon-wrapper--medium\"\n [ngStyle]=\"{ 'background-color': formGroup.value.color }\"\n >\n <i\n class=\"stroked-icon\"\n [c8yIcon]=\"timelineType === 'EVENT' ? 'online1' : 'bell'\"\n ></i>\n </span>\n </div>\n </div>\n <button\n class=\"btn-clean text-truncate\"\n [title]=\"formGroup.value.details?.label\"\n (click)=\"\n allowItemEdit\n ? (li.collapsed = !li.collapsed)\n : showActiveToggle\n ? toggleActive()\n : addOrRemoveItem()\n \"\n >\n <span class=\"text-truncate\">\n <c8y-highlight\n [text]=\"\n omitProperties.label\n ? formGroup.value.details?.filters?.type\n : formGroup.value.details?.label\n \"\n [pattern]=\"highlightText\"\n [shouldTrimPattern]=\"true\"\n ></c8y-highlight>\n </span>\n <small\n class=\"text-truncate text-muted icon-flex\"\n *ngIf=\"formGroup.value.__target && !hideSource\"\n >\n <i c8yIcon=\"exchange\"></i>\n <span class=\"text-truncate\">{{ formGroup.value.__target.name }}</span>\n </small>\n </button>\n\n <button\n class=\"btn-dot btn-dot--danger m-l-auto\"\n title=\"{{ 'Invalid entries' | translate }}\"\n [popover]=\"'Some entries are invalid. Check the required input fields.' | translate\"\n container=\"body\"\n *ngIf=\"(valid$ | async) === false && li.collapsed\"\n [outsideClick]=\"true\"\n >\n <i c8yIcon=\"exclamation-circle\"></i>\n </button>\n </div>\n\n <c8y-li-action\n [icon]=\"'minus-circle'\"\n *ngIf=\"optionToRemove\"\n [label]=\"'Remove from list' | translate\"\n (click)=\"remove()\"\n ></c8y-li-action>\n <c8y-li-collapse *ngIf=\"allowItemEdit\">\n <div class=\"data-point-details\">\n <c8y-alarm-event-attributes-form\n formControlName=\"details\"\n [timelineType]=\"timelineType\"\n [omitProperties]=\"omitProperties\"\n [datapoints]=\"datapoints\"\n [target]=\"formGroup.value.__target\"\n ></c8y-alarm-event-attributes-form>\n </div>\n </c8y-li-collapse>\n</c8y-li>\n" }]
}], ctorParameters: () => [{ type: i1.FormBuilder }], propDecorators: { timelineType: [{
type: Input
}], datapoints: [{
type: Input
}], highlightText: [{
type: Input
}], showAddRemoveButton: [{
type: Input
}], isSelected: [{
type: Input
}], optionToRemove: [{
type: Input
}], showActiveToggle: [{
type: Input
}], allowItemEdit: [{
type: Input
}], hideSource: [{
type: Input
}], displayAsSwitch: [{
type: Input
}], omitProperties: [{
type: Input
}], added: [{
type: Output
}], removed: [{
type: Output
}] } });
class CustomAlarmEventFormComponent {
constructor(formBuilder) {
this.formBuilder = formBuilder;
this.omitProperties = {};
this.selectedItems = [];
this.added = new EventEmitter();
this.cancel = new EventEmitter();
this.destroy$ = new Subject();
this.formGroup = this.formBuilder.group({
details: [],
color: [],
__active: [],
__target: []
});
this.valid$ = this.formGroup.statusChanges.pipe(takeUntil(this.destroy$), map(val => val === 'VALID'), startWith(false));
}
ngOnInit() {
this.formGroup.patchValue({
color: this.defaultColor,
__target: this.target,
details: { timelineType: this.timelineType }
});
}
ngOnDestroy() {
this.destroy$.next();
this.destroy$.complete();
}
add() {
if (this.formGroup.valid) {
const formValue = this.transformFormValue(this.formGroup.value);
this.added.emit(formValue);
}
}
transformFormValue(formValue) {
const obj = Object.assign({}, formValue.details || {}, formValue);
delete obj.details;
return obj;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: CustomAlarmEventFormComponent, deps: [{ token: i1.FormBuilder }], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.14", type: CustomAlarmEventFormComponent, isStandalone: false, selector: "c8y-custom-alarm-event-form", inputs: { timelineType: "timelineType", target: "target", omitProperties: "omitProperties", selectedItems: "selectedItems", defaultColor: "defaultColor" }, outputs: { added: "added", cancel: "cancel" }, ngImport: i0, template: "<form [formGroup]=\"formGroup\" class=\"p-16\">\n <div\n *ngIf=\"!omitProperties.color\"\n class=\"form-group d-flex a-i-center gap-8\"\n [title]=\"'Change color' | translate\"\n >\n <label class=\"m-0\">{{ 'Color' | translate }}</label>\n <div class=\"c8y-colorpicker\"\n [ngClass]=\"{\n 'c8y-colorpicker--event': timelineType === 'EVENT',\n 'c8y-colorpicker--alarm': timelineType !== 'EVENT'\n }\"\n >\n <input\n type=\"color\"\n formControlName=\"color\"\n (click)=\"$event.stopPropagation()\"\n />\n <span [style.background-color]=\"formGroup.value.color\">\n <i [c8yIcon]=\"timelineType === 'EVENT' ? 'online1' : 'bell'\"></i>\n </span>\n </div>\n </div>\n\n <c8y-alarm-event-attributes-form\n formControlName=\"details\"\n [timelineType]=\"timelineType\"\n [omitProperties]=\"omitProperties\"\n [selectedItems]=\"selectedItems\"\n ></c8y-alarm-event-attributes-form>\n\n <div class=\"d-flex p-t-16\">\n <button class=\"btn btn-default btn-sm\" (click)=\"cancel.emit()\">\n {{ 'Cancel' | translate }}\n </button>\n <button\n class=\"btn btn-primary btn-sm\"\n [disabled]=\"(valid$ | async) === false\"\n (click)=\"add()\"\n >\n <i c8yIcon=\"plus-circle\"></i>\n {{ 'Select' | translate }}\n </button>\n </div>\n</form>\n", dependencies: [{ kind: "directive", type: i2.IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "directive", type: i3.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i3.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2.RequiredInputPlaceholderDirective, selector: "input[required], input[formControlName]" }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "component", type: AlarmEventAttributesFormComponent, selector: "c8y-alarm-event-attributes-form", inputs: ["timelineType", "omitProperties", "selectedItems", "target", "datapoints"] }, { kind: "pipe", type: i2.C8yTranslatePipe, name: "translate" }, { kind: "pipe", type: i3.AsyncPipe, name: "async" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: CustomAlarmEventFormComponent, decorators: [{
type: Component,
args: [{ selector: 'c8y-custom-alarm-event-form', standalone: false, template: "<form [formGroup]=\"formGroup\" class=\"p-16\">\n <div\n *ngIf=\"!omitProperties.color\"\n class=\"form-group d-flex a-i-center gap-8\"\n [title]=\"'Change color' | translate\"\n >\n <label class=\"m-0\">{{ 'Color' | translate }}</label>\n <div class=\"c8y-colorpicker\"\n [ngClass]=\"{\n 'c8y-colorpicker--event': timelineType === 'EVENT',\n 'c8y-colorpicker--alarm': timelineType !== 'EVENT'\n }\"\n >\n <input\n type=\"color\"\n formControlName=\"color\"\n (click)=\"$event.stopPropagation()\"\n />\n <span [style.background-color]=\"formGroup.value.color\">\n <i [c8yIcon]=\"timelineType === 'EVENT' ? 'online1' : 'bell'\"></i>\n </span>\n </div>\n </div>\n\n <c8y-alarm-event-attributes-form\n formControlName=\"details\"\n [timelineType]=\"timelineType\"\n [omitProperties]=\"omitProperties\"\n [selectedItems]=\"selectedItems\"\n ></c8y-alarm-event-attributes-form>\n\n <div class=\"d-flex p-t-16\">\n <button class=\"btn btn-default btn-sm\" (click)=\"cancel.emit()\">\n {{ 'Cancel' | translate }}\n </button>\n <button\n class=\"btn btn-primary btn-sm\"\n [disabled]=\"(valid$ | async) === false\"\n (click)=\"add()\"\n >\n <i c8yIcon=\"plus-circle\"></i>\n {{ 'Select' | translate }}\n </button>\n </div>\n</form>\n" }]
}], ctorParameters: () => [{ type: i1.FormBuilder }], propDecorators: { timelineType: [{
type: Input
}], target: [{
type: Input
}], omitProperties: [{
type: Input
}], selectedItems: [{
type: Input
}], defaultColor: [{
type: Input
}], added: [{
type: Output
}], cancel: [{
type: Output
}] } });
class IncludesAlarmOrEventPipe {
transform(itemList, item) {
if (!Array.isArray(itemList) || !item) {
return false;
}
return itemList.some(tmp => tmp.label === item.label &&
tmp.filters.type === item.filters.type &&
tmp.__target?.id === item.__target?.id);
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: IncludesAlarmOrEventPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "19.2.14", ngImport: i0, type: IncludesAlarmOrEventPipe, isStandalone: false, name: "includesAlarmOrEvent" }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: IncludesAlarmOrEventPipe, decorators: [{
type: Pipe,
args: [{
name: 'includesAlarmOrEvent',
standalone: false
}]
}] });
class AlarmEventSelectorComponent {
constructor(alarmEventSelectorService) {
this.alarmEventSelectorService = alarmEventSelectorService;
this.timelineType = 'ALARM';
this.allowChangingContext = true;
this.selectedItems = new Array();
this.allowSearch = true;
this.groupsSelectable = true;
this.hideSource = false;
this.omitProperties = {};
this.selectionChange = new EventEmitter();
this.filterString = '';
this.maxNumberOfItems = 50;
this.loadingItems$ = new BehaviorSubject(false);
this.assetSelection = new BehaviorSubject({});
this.isExpanded = false;
this.defaultColor = getComputedStyle(document.documentElement).getPropertyValue('--brand-primary') ||
getComputedStyle(document.documentElement).getPropertyValue('--c8y-brand-primary') ||
'#1776BF';
this.filterString$ = new BehaviorSubject('');
}
ngOnInit() {
this.timelineTypeTexts = this.alarmEventSelectorService.timelineTypeTexts(this.timelineType);
this.setupObservables();
if (this.contextAsset) {
this.selectAsset(this.contextAsset);
}
}
itemAdded(item) {
item.__active = true;
this.selectedItems = [...this.selectedItems, item];
this.emitCurrentSelection();
}
itemRemoved(alarm) {
this.selectedItems = this.selectedItems.filter(tmp => tmp.label !== alarm.label ||
tmp.filters.type !== alarm.filters.type ||
tmp.__target?.id !== alarm.__target?.id);
this.emitCurrentSelection();
}
assetSelectionChanged(evt) {
if (evt.items) {
return this.selectAsset(evt.items.length ? evt.items[0] : evt.items);
}
// reset selection
this.assetSelection.next(null);
}
trackByFn(_index, item) {
return `${item.filters.type}-${item.__target?.id}`;
}
filterStringChanged(newValue = '') {
this.filterString$.next(newValue);
this.filterString = newValue;
}
setLoadingState(state) {
queueMicrotask(() => this.loadingItems$.next(state));
}
setupObservables() {
this.items$ = this.assetSelection.pipe(tap(() => this.setLoadingState(true)), switchMap((asset) => asset?.id
? this.alarmEventSelectorService.getAlarmsOrEvents(asset, this.timelineType)
: this.alarmEventSelectorService.getItems(this.timelineType)), tap(() => this.setLoadingState(false)), map(items => {
if (this.omitProperties.color) {
items.forEach(i => (i.color = this.defaultColor));
}
return items;
}), shareReplay(1));
this.filterStringChanges$ = this.filterString$.pipe(distinctUntilChanged(), debounceTime(500), shareReplay(1));
this.filteredItems$ = combineLatest([this.filterStringChanges$, this.items$]).pipe(map(([filterString, items]) => {
if (!filterString) {
return items;
}
const lowerCaseFilterString = filterString.toLowerCase();
return items.filter(item => this.includesFilterString(item, lowerCaseFilterString));
}), map(filtered => filtered.slice(0, this.maxNumberOfItems)));
}
selectAsset(asset) {
this.assetSelection.next(asset);
this.filterStringChanged();
}
emitCurrentSelection() {
this.selectionChange.emit(this.selectedItems);
}
includesFilterString(alarm, lowerCaseFilterString) {
const label = alarm.label?.toLowerCase();
if (label && label.includes(lowerCaseFilterString)) {
return true;
}
const type = alarm.filters.type?.toLowerCase();
if (type && type.includes(lowerCaseFilterString)) {
return true;
}
return false;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AlarmEventSelectorComponent, deps: [{ token: AlarmEventSelectorService }], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.14", type: AlarmEventSelectorComponent, isStandalone: false, selector: "c8y-alarm-event-selector", inputs: { timelineType: "timelineType", contextAsset: "contextAsset", allowChangingContext: "allowChangingContext", selectedItems: "selectedItems", allowSearch: "allowSearch", groupsSelectable: "groupsSelectable", hideSource: "hideSource", omitProperties: "omitProperties" }, outputs: { selectionChange: "selectionChange" }, providers: [
{
provide: NG_VALUE_ACCESSOR,
multi: true,
useExisting: forwardRef(() => AlarmEventSelectorComponent)
}
], ngImport: i0, template: "<div\n class=\"d-grid grid__row--1 fit-h\"\n [ngClass]=\"{\n 'grid__col--3-6-3--md': allowChangingContext,\n 'grid__col--8-4--md': !allowChangingContext\n }\"\n>\n <div\n class=\"d-flex d-col p-relative bg-level-1\"\n *ngIf=\"allowChangingContext\"\n >\n <c8y-asset-selector-miller\n class=\"d-contents\"\n [(ngModel)]=\"contextAsset\"\n [asset]=\"contextAsset\"\n (onSelected)=\"assetSelectionChanged($event)\"\n [container]=\"''\"\n [config]=\"{\n view: 'miller',\n groupsSelectable: groupsSelectable,\n columnHeaders: true,\n showChildDevices: true,\n showUnassignedDevices: true,\n singleColumn: true,\n search: allowSearch,\n showFilter: true\n }\"\n ></c8y-asset-selector-miller>\n </div>\n <!-- center column -->\n <div class=\"inner-scroll bg-component\" data-cy=\"c8y-alarm-event-selector--inner-column\">\n <ng-template #noDeviceEmptyState>\n <div class=\"p-16\">\n <c8y-ui-empty-state\n [icon]=\"timelineTypeTexts.emptyStateIcon\"\n [title]=\"timelineTypeTexts.emptyStateTitle | translate\"\n [subtitle]=\"timelineTypeTexts.assetWithNoItemsEmptyStateSubtitle | translate\"\n [horizontal]=\"true\"\n ></c8y-ui-empty-state>\n </div>\n </ng-template>\n\n <ng-template #loadingData>\n <div class=\"p-16 text-center\">\n <c8y-loading></c8y-loading>\n </div>\n </ng-template>\n\n <div\n