UNPKG

@c8y/ngx-components

Version:

Angular modules for Cumulocity IoT applications

464 lines (459 loc) 169 kB
import { moveItemInArray, CdkDropList, CdkDrag, CdkDragHandle, DragDropModule } from '@angular/cdk/drag-drop'; import * as i0 from '@angular/core'; import { Injectable, forwardRef, Input, Component, EventEmitter, ContentChild, Output, Pipe, Optional, NgModule, inject, DestroyRef } from '@angular/core'; import * as i2$1 from '@c8y/ngx-components'; import { C8yValidators, C8yTranslateDirective, FormGroupComponent, RequiredInputPlaceholderDirective, MessagesComponent, MessageDirective, C8yTranslatePipe, MAX_PAGE_SIZE, IconDirective, ListItemComponent, ListItemDragHandleComponent, ListItemCheckboxComponent, HighlightComponent, ListItemActionComponent, ListItemCollapseComponent, TypeaheadComponent, ForOfDirective, ListItemIconComponent, EmptyStateComponent, LoadingComponent, ListGroupComponent, CoreModule, FormsModule as FormsModule$1 } from '@c8y/ngx-components'; import { MillerViewComponent, AssetSelectorModule } from '@c8y/ngx-components/assets-navigator'; import { CollapseModule } from 'ngx-bootstrap/collapse'; import { BsDropdownModule } from 'ngx-bootstrap/dropdown'; import * as i1$1 from 'ngx-bootstrap/modal'; import { ModalModule } from 'ngx-bootstrap/modal'; import { PopoverDirective, PopoverModule } from 'ngx-bootstrap/popover'; import { TooltipDirective, TooltipModule } from 'ngx-bootstrap/tooltip'; import * as i2 from '@angular/forms'; import { Validators, FormsModule, ReactiveFormsModule, NG_VALUE_ACCESSOR, NG_VALIDATORS, NgForm, ControlContainer } from '@angular/forms'; import { map, take, filter, startWith, tap, switchMap, shareReplay, distinctUntilChanged, debounceTime } from 'rxjs/operators'; import { gettext } from '@c8y/ngx-components/gettext'; import { NgClass, NgTemplateOutlet, KeyValuePipe, AsyncPipe, NgIf, NgFor } from '@angular/common'; import * as i4 from '@c8y/ngx-components/context-dashboard'; import { WidgetConfigService, WidgetConfigComponent, ContextDashboardComponent, WidgetConfigFeedbackComponent } from '@c8y/ngx-components/context-dashboard'; import { pipe, BehaviorSubject, from, combineLatest, filter as filter$1, take as take$1, map as map$1, switchMap as switchMap$1 } from 'rxjs'; import * as i1 from '@c8y/client'; import { MeasurementService } from '@c8y/client'; import { sortBy, get, uniq } from 'lodash-es'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; const AXIS_TYPES = [ { val: null, text: gettext('Auto') }, { val: 'left', text: gettext('Left') }, { val: 'right', text: gettext('Right') } ]; const CHART_LINE_TYPES = [ { val: 'line', text: gettext('Line') }, { val: 'points', text: gettext('Points') }, { val: 'linePoints', text: gettext('Line and points') }, { val: 'bars', text: gettext('Bars') }, { val: 'step-before', text: gettext('Step before') }, { val: 'step-after', text: gettext('Step after') } ]; const CHART_RENDER_TYPES = [ { val: 'min', text: gettext('Minimum') }, { val: 'max', text: gettext('Maximum') }, { val: 'area', text: gettext('Minimum and maximum') }, { val: 'avg', text: gettext('Average') }, { val: 'count', text: gettext('Count') }, { val: 'sum', text: gettext('Sum') } ]; class DatapointAttributesFormValidationService { constructor(formBuilder) { this.formBuilder = formBuilder; } getDefaultFormGroup(fieldsToRemove = []) { const formFields = { __active: [true, []], __target: this.getTargetFormGroup(), __template: [undefined, []], color: ['', this.getColorValidators()], label: ['', this.getLabelValidators()], description: ['', this.getDescriptionValidators()], fragment: ['', this.getFragmentValidators()], series: ['', this.getSeriesValidators()], range: this.getMinMaxFormGroup(), unit: [undefined, this.getUnitValidators()], target: [undefined, this.getTargetValidators()], redRange: this.getMinMaxFormGroup(), yellowRange: this.getMinMaxFormGroup(), chart: this.getChartFormGroup(), display: this.getDisplayFormGroup() }; if (fieldsToRemove.length) { for (const field of fieldsToRemove) { delete formFields[field]; } } return this.formBuilder.group(formFields, { validators: this.getOverallValidators() }); } convertToBackendFormat(formDataStructure, showChart) { if (!formDataStructure) { return {}; } const { __active, __target, __template, color, label, description, fragment, series, range, unit, target, redRange, yellowRange, chart, display } = formDataStructure; const obj = { __active, __target, __template, color, label, description, fragment, series, min: range?.min, max: range?.max, unit, target, redRangeMin: redRange?.min, redRangeMax: redRange?.max, yellowRangeMin: yellowRange?.min, yellowRangeMax: yellowRange?.max, renderType: showChart ? chart?.renderType : display?.renderType, lineType: chart?.lineType, yAxisType: chart?.yAxisType }; return obj; } convertToFormGroupFormat(backendDataStructure) { if (!backendDataStructure) { return {}; } const { __active, __target, __template, color, label, description, fragment, series, min, max, unit, target, redRangeMin, redRangeMax, yellowRangeMin, yellowRangeMax, renderType, lineType, yAxisType } = backendDataStructure; const obj = { __active, __target, __template, color, label, description, fragment, series, range: { min: this.convertStringToNumber(min), max: this.convertStringToNumber(max) }, unit, target: this.convertStringToNumber(target), redRange: { min: this.convertStringToNumber(redRangeMin), max: this.convertStringToNumber(redRangeMax) }, yellowRange: { min: this.convertStringToNumber(yellowRangeMin), max: this.convertStringToNumber(yellowRangeMax) }, chart: renderType || lineType || yAxisType ? { renderType, lineType, yAxisType } : undefined, display: renderType ? { renderType } : undefined }; return obj; } getColorValidators() { return [Validators.required, Validators.minLength(4)]; } getLabelValidators() { return [Validators.required, Validators.minLength(1)]; } getDescriptionValidators() { return []; } getFragmentValidators() { return [Validators.required, Validators.minLength(1), Validators.pattern(/^[^.]*$/)]; } getSeriesValidators() { return [ Validators.required, Validators.minLength(1), (control) => { const forbidden = control.value?.includes('.') || ''; return forbidden ? { noPeriods: { message: gettext('Series cannot contain periods.') } } : null; } ]; } getMinMaxValidators() { return [C8yValidators.minMaxValidator(), C8yValidators.requireBothMinAndMax()]; } getUnitValidators() { return []; } getTargetValidators() { return []; } getOverallValidators() { return [ C8yValidators.withinScale('redRange.min'), C8yValidators.withinScale('redRange.max'), C8yValidators.withinScale('yellowRange.min'), C8yValidators.withinScale('yellowRange.max'), C8yValidators.withinScale('target') ]; } getMinMaxFormGroup() { return this.formBuilder.group({ min: [undefined, []], max: [undefined, []] }, { validators: this.getMinMaxValidators() }); } getChartFormGroup() { return this.formBuilder.group({ renderType: [CHART_RENDER_TYPES[0].val, []], lineType: [CHART_LINE_TYPES[0].val, []], yAxisType: [AXIS_TYPES[0].val, []] }); } getDisplayFormGroup() { return this.formBuilder.group({ renderType: [CHART_RENDER_TYPES[0].val, []] }); } getTargetFormGroup() { return this.formBuilder.group({ id: [undefined, []], name: [undefined, []] }); } convertStringToNumber(possibleString) { if (typeof possibleString === 'string') { try { return Number.parseFloat(possibleString); } catch { return undefined; } } else { return possibleString; } } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: DatapointAttributesFormValidationService, deps: [{ token: i2.FormBuilder }], target: i0.ɵɵFactoryTarget.Injectable }); } static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: DatapointAttributesFormValidationService, providedIn: 'root' }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: DatapointAttributesFormValidationService, decorators: [{ type: Injectable, args: [{ providedIn: 'root' }] }], ctorParameters: () => [{ type: i2.FormBuilder }] }); class DatapointAttributesFormComponent { constructor(formValidations) { this.formValidations = formValidations; this.selectableChartRenderTypes = []; this.selectableChartLineTypes = []; this.selectableAxisTypes = []; this.showTarget = true; this.showRange = true; this.showYellowRange = true; this.showRedRange = true; this.showChart = true; this.showFormIfTemplateWasSelected = false; this.showAdvancedChartOptions = false; /** Displays all errors even if the form was not touched yet. */ this.showErrorsImmediately = false; this.rawValue = {}; this.CHART_RENDER_TYPES = Array.from(CHART_RENDER_TYPES); this.CHART_LINE_TYPES = Array.from(CHART_LINE_TYPES); this.AXIS_TYPES = Array.from(AXIS_TYPES); this.DEFAULT_RENDER_TYPES = ['min', 'max', 'area']; this.customValidationErrorMessages = {}; this.shouldForceInitialValidation = true; this.formGroup = this.formValidations.getDefaultFormGroup(); this.setSubForms(); } ngOnInit() { this.initializeFormVisibility(); this.filterChartTypes(); } ngOnChanges(changes) { if (changes.showAdvancedChartOptions) { this.filterChartTypes(); } } ngAfterViewInit() { if (this.showErrorsImmediately) { this.formGroup.markAllAsTouched(); } } validate(_control) { if (this.formGroup?.get('series')?.errors?.noPeriods) { return this.formGroup?.get('series')?.errors; } return this.formGroup?.valid ? null : { formInvalid: {} }; } writeValue(obj) { this.rawValue = obj || {}; if (obj) { this.formGroup.patchValue(this.formValidations.convertToFormGroupFormat(obj), { emitEvent: false }); } if (this.shouldForceInitialValidation) { queueMicrotask(() => { this.formGroup.updateValueAndValidity(); }); this.shouldForceInitialValidation = false; } } registerOnChange(fn) { this.formGroup.valueChanges .pipe(map(formValue => this.formValidations.convertToBackendFormat(formValue, this.showChart)), map(formValue => Object.assign(this.rawValue, formValue))) .subscribe(fn); } registerOnTouched(fn) { this.formGroup.valueChanges.pipe(take(1)).subscribe(fn); } setDisabledState(isDisabled) { if (this.formGroup?.disabled === isDisabled) { return; } isDisabled ? this.formGroup.disable() : this.formGroup.enable(); } setSubForms() { if (!this.formGroup) { this.range = this.yellowRange = this.redRange = this.chart = undefined; return; } this.range = this.formGroup.get('range'); this.yellowRange = this.formGroup.get('yellowRange'); this.redRange = this.formGroup.get('redRange'); this.chart = this.formGroup.get('chart'); this.display = this.formGroup.get('display'); } initializeFormVisibility() { this.showChartForm = this.chart && this.showChart; const hasLineTypes = !!this.selectableChartLineTypes?.length; const hasAxisTypes = !!this.selectableAxisTypes?.length; const hasRenderTypes = this.selectableChartRenderTypes?.length > 0 || this.selectableChartRenderTypes === undefined; this.showOnlyDisplayForm = !this.showChartForm && !hasLineTypes && !hasAxisTypes && hasRenderTypes; } filterChartTypes() { this.filterRenderTypes(); this.filterLineTypes(); this.filterAxisTypes(); } filterRenderTypes() { let renderTypes = Array.from(CHART_RENDER_TYPES); if (!this.showAdvancedChartOptions) { renderTypes = renderTypes.filter(rt => this.DEFAULT_RENDER_TYPES.includes(rt.val)); } if (this.selectableChartRenderTypes?.length) { renderTypes = renderTypes.filter(rt => this.selectableChartRenderTypes.includes(rt.val)); } this.CHART_RENDER_TYPES = renderTypes; } filterLineTypes() { if (!!this.selectableChartLineTypes?.length) { this.CHART_LINE_TYPES = this.CHART_LINE_TYPES.filter(lineType => this.selectableChartLineTypes.includes(lineType.val)); } } filterAxisTypes() { if (!!this.selectableAxisTypes?.length) { this.AXIS_TYPES = this.AXIS_TYPES.filter(axisType => this.selectableAxisTypes.includes(axisType.val)); } } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: DatapointAttributesFormComponent, deps: [{ token: DatapointAttributesFormValidationService }], target: i0.ɵɵFactoryTarget.Component }); } static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.19", type: DatapointAttributesFormComponent, isStandalone: true, selector: "c8y-datapoint-attributes-form", inputs: { selectableChartRenderTypes: "selectableChartRenderTypes", selectableChartLineTypes: "selectableChartLineTypes", selectableAxisTypes: "selectableAxisTypes", showTarget: "showTarget", showRange: "showRange", showYellowRange: "showYellowRange", showRedRange: "showRedRange", showChart: "showChart", showFormIfTemplateWasSelected: "showFormIfTemplateWasSelected", showAdvancedChartOptions: "showAdvancedChartOptions", showErrorsImmediately: "showErrorsImmediately" }, providers: [ { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => DatapointAttributesFormComponent), multi: true }, { provide: NG_VALIDATORS, useExisting: forwardRef(() => DatapointAttributesFormComponent), multi: true } ], usesOnChanges: true, ngImport: i0, template: "<div [formGroup]=\"formGroup\">\n @if (!rawValue?.__template || showFormIfTemplateWasSelected) {\n @if (formGroup.controls?.label || formGroup.controls?.unit || formGroup.controls?.target) {\n <fieldset class=\"c8y-fieldset\">\n <legend translate>Details</legend>\n <div class=\"row\">\n @if (formGroup.controls?.label) {\n <div class=\"col-md-6\">\n <c8y-form-group class=\"form-group-sm\">\n <label translate>Label</label>\n <input\n class=\"form-control\"\n name=\"label\"\n formControlName=\"label\"\n [placeholder]=\"'e.g. {{ example }}' | translate: { example: 'Temperature' }\"\n />\n <c8y-messages\n [show]=\"formGroup.controls.label.touched && formGroup.controls.label.errors\"\n >\n @for (item of customValidationErrorMessages['label'] | keyvalue; track item.key) {\n <c8y-message\n [name]=\"item.key\"\n [text]=\"item.value\"\n ></c8y-message>\n }\n </c8y-messages>\n </c8y-form-group>\n </div>\n }\n @if (formGroup.controls?.unit) {\n <div class=\"col-md-6\">\n <c8y-form-group class=\"form-group-sm\">\n <label translate>Unit</label>\n <input\n class=\"form-control\"\n name=\"unit\"\n formControlName=\"unit\"\n [placeholder]=\"'e.g. {{ example }}' | translate: { example: '\u00BAC' }\"\n />\n <c8y-messages\n [show]=\"formGroup.controls.unit.touched && formGroup.controls.unit.errors\"\n >\n @for (item of customValidationErrorMessages['unit'] | keyvalue; track item.key) {\n <c8y-message\n [name]=\"item.key\"\n [text]=\"item.value\"\n ></c8y-message>\n }\n </c8y-messages>\n </c8y-form-group>\n </div>\n }\n @if (formGroup.controls?.target && showTarget) {\n <div class=\"col-md-6\">\n <c8y-form-group\n class=\"form-group-sm\"\n [ngClass]=\"{\n 'has-error':\n (range?.touched || formGroup.controls.target.touched) &&\n formGroup.controls?.target?.errors\n }\"\n >\n <label translate>Target</label>\n <input\n class=\"form-control\"\n name=\"target\"\n type=\"number\"\n formControlName=\"target\"\n [placeholder]=\"'e.g. {{ example }}' | translate: { example: 25 }\"\n />\n <c8y-messages\n [show]=\"\n (range?.touched || formGroup.controls.target.touched) &&\n formGroup.controls.target.errors\n \"\n >\n @for (\n item of customValidationErrorMessages['target'] | keyvalue;\n track item.key\n ) {\n <c8y-message\n [name]=\"item.key\"\n [text]=\"item.value\"\n ></c8y-message>\n }\n </c8y-messages>\n </c8y-form-group>\n </div>\n }\n </div>\n </fieldset>\n }\n @if (range && showRange) {\n <fieldset class=\"c8y-fieldset\">\n <legend translate>Range</legend>\n <div\n class=\"row\"\n formGroupName=\"range\"\n >\n <div class=\"col-md-6\">\n <c8y-form-group\n class=\"form-group-sm\"\n [ngClass]=\"{ 'has-error': range?.touched && range?.controls?.min?.errors }\"\n >\n <label translate>Min</label>\n <input\n class=\"form-control\"\n name=\"min\"\n type=\"number\"\n formControlName=\"min\"\n [placeholder]=\"'e.g. {{ example }}' | translate: { example: 0 }\"\n />\n <c8y-messages [show]=\"range?.touched && range.controls?.min?.errors\"></c8y-messages>\n </c8y-form-group>\n </div>\n <div class=\"col-md-6\">\n <c8y-form-group\n class=\"form-group-sm\"\n [ngClass]=\"{ 'has-error': range?.touched && range?.controls?.max?.errors }\"\n >\n <label translate>Max</label>\n <input\n class=\"form-control\"\n name=\"max\"\n type=\"number\"\n formControlName=\"max\"\n [placeholder]=\"'e.g. {{ example }}' | translate: { example: 100 }\"\n />\n <c8y-messages [show]=\"range?.touched && range.controls?.max?.errors\">\n @for (item of customValidationErrorMessages['max'] | keyvalue; track item.key) {\n <c8y-message\n [name]=\"item.key\"\n [text]=\"item.value\"\n ></c8y-message>\n }\n </c8y-messages>\n </c8y-form-group>\n </div>\n </div>\n </fieldset>\n }\n @if (yellowRange && showYellowRange) {\n <fieldset\n class=\"c8y-fieldset\"\n formGroupName=\"yellowRange\"\n >\n <legend translate>Yellow range</legend>\n <div class=\"row\">\n <div class=\"col-md-6\">\n <c8y-form-group\n class=\"form-group-sm\"\n [ngClass]=\"{\n 'has-error':\n (range?.touched || yellowRange?.touched) && yellowRange?.controls?.min?.errors\n }\"\n >\n <label translate>Min</label>\n <input\n class=\"form-control\"\n name=\"min\"\n type=\"number\"\n formControlName=\"min\"\n [placeholder]=\"'e.g. {{ example }}' | translate: { example: 50 }\"\n />\n <c8y-messages\n [show]=\"\n (range?.touched || yellowRange?.touched) && yellowRange.controls?.min?.errors\n \"\n ></c8y-messages>\n </c8y-form-group>\n </div>\n\n <div class=\"col-md-6\">\n <c8y-form-group\n class=\"form-group-sm\"\n [ngClass]=\"{\n 'has-error':\n (range?.touched || yellowRange?.touched) && yellowRange?.controls?.max?.errors\n }\"\n >\n <label translate>Max</label>\n <input\n class=\"form-control\"\n name=\"max\"\n type=\"number\"\n formControlName=\"max\"\n [placeholder]=\"'e.g. {{ example }}' | translate: { example: 75 }\"\n />\n <c8y-messages\n [show]=\"\n (range?.touched || yellowRange?.touched) && yellowRange.controls?.max?.errors\n \"\n ></c8y-messages>\n </c8y-form-group>\n </div>\n </div>\n </fieldset>\n }\n @if (redRange && showRedRange) {\n <fieldset\n class=\"c8y-fieldset\"\n formGroupName=\"redRange\"\n >\n <legend translate>Red range</legend>\n <div class=\"row\">\n <div class=\"col-md-6\">\n <c8y-form-group\n class=\"form-group-sm\"\n [ngClass]=\"{\n 'has-error':\n (range?.touched || redRange?.touched) && redRange?.controls?.min?.errors\n }\"\n >\n <label translate>Min</label>\n <input\n class=\"form-control\"\n name=\"min\"\n type=\"number\"\n formControlName=\"min\"\n [placeholder]=\"'e.g. {{ example }}' | translate: { example: 75 }\"\n />\n <c8y-messages\n [show]=\"(range?.touched || redRange?.touched) && redRange.controls?.min?.errors\"\n ></c8y-messages>\n </c8y-form-group>\n </div>\n\n <div class=\"col-md-6\">\n <c8y-form-group\n class=\"form-group-sm\"\n [ngClass]=\"{\n 'has-error':\n (range?.touched || redRange?.touched) && redRange?.controls?.max?.errors\n }\"\n >\n <label translate>Max</label>\n <input\n class=\"form-control\"\n name=\"max\"\n type=\"number\"\n formControlName=\"max\"\n [placeholder]=\"'e.g. {{ example }}' | translate: { example: 100 }\"\n />\n <c8y-messages\n [show]=\"(range?.touched || redRange?.touched) && redRange.controls?.max?.errors\"\n ></c8y-messages>\n </c8y-form-group>\n </div>\n </div>\n </fieldset>\n }\n }\n\n @if (showChartForm) {\n <fieldset\n class=\"c8y-fieldset\"\n formGroupName=\"chart\"\n >\n <legend translate>Chart</legend>\n <div class=\"tight-grid\">\n @if (selectableChartRenderTypes?.length !== 0) {\n <div class=\"col-xs-6 col-sm-4\">\n <c8y-form-group class=\"form-group-sm\">\n <ng-container *ngTemplateOutlet=\"displayHelpButton\"></ng-container>\n <div class=\"c8y-select-wrapper\">\n <select\n class=\"form-control\"\n formControlName=\"renderType\"\n >\n @for (type of CHART_RENDER_TYPES; track type.val) {\n <option [ngValue]=\"type.val\">\n {{ type.text | translate }}\n </option>\n }\n </select>\n </div>\n </c8y-form-group>\n </div>\n }\n @if (selectableChartLineTypes?.length !== 0) {\n <div class=\"col-xs-6 col-sm-4\">\n <c8y-form-group class=\"form-group-sm\">\n <label\n for=\"chartType\"\n translate\n >\n Chart type\n </label>\n <div class=\"c8y-select-wrapper\">\n <select\n class=\"form-control\"\n formControlName=\"lineType\"\n >\n @for (type of CHART_LINE_TYPES; track type.val) {\n <option [ngValue]=\"type.val\">\n {{ type.text | translate }}\n </option>\n }\n </select>\n <span></span>\n </div>\n </c8y-form-group>\n </div>\n }\n @if (selectableAxisTypes?.length !== 0) {\n <div class=\"col-xs-6 col-sm-4\">\n <c8y-form-group class=\"form-group-sm\">\n <label\n for=\"yAxis\"\n translate\n >\n Y-axis\n </label>\n <div class=\"c8y-select-wrapper\">\n <select\n class=\"form-control\"\n formControlName=\"yAxisType\"\n >\n @for (type of AXIS_TYPES; track type.val) {\n <option [ngValue]=\"type.val\">\n {{ type.text | translate }}\n </option>\n }\n </select>\n <span></span>\n </div>\n </c8y-form-group>\n </div>\n }\n </div>\n </fieldset>\n }\n @if (showOnlyDisplayForm) {\n <fieldset\n class=\"c8y-fieldset\"\n formGroupName=\"display\"\n >\n <legend>\n <ng-container *ngTemplateOutlet=\"displayHelpButton\"></ng-container>\n </legend>\n <div class=\"tight-grid\">\n <div class=\"col-xs-6 col-sm-4\">\n <c8y-form-group class=\"form-group-sm\">\n <div class=\"c8y-select-wrapper\">\n <select\n class=\"form-control\"\n formControlName=\"renderType\"\n >\n @for (type of CHART_RENDER_TYPES; track type.val) {\n <option [ngValue]=\"type.val\">\n {{ type.text | translate }}\n </option>\n }\n </select>\n </div>\n </c8y-form-group>\n </div>\n </div>\n </fieldset>\n }\n</div>\n\n<ng-template #displayHelpButton>\n <label>\n {{ 'Display' | translate }}\n <button\n class=\"btn-help btn-help--sm\"\n [attr.aria-label]=\"'Help' | translate\"\n popover=\"{{ 'Value displayed when data is aggregated' | translate }}\"\n placement=\"right\"\n triggers=\"focus\"\n type=\"button\"\n ></button>\n </label>\n</ng-template>\n", dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.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: i2.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i2.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "directive", type: i2.FormGroupName, selector: "[formGroupName]", inputs: ["formGroupName"] }, { kind: "directive", type: C8yTranslateDirective, selector: "[translate],[ngx-translate]" }, { kind: "component", type: FormGroupComponent, selector: "c8y-form-group", inputs: ["hasError", "hasWarning", "hasSuccess", "novalidation", "status"] }, { kind: "directive", type: RequiredInputPlaceholderDirective, selector: "input[required], input[formControlName]" }, { kind: "component", type: MessagesComponent, selector: "c8y-messages", inputs: ["show", "defaults", "helpMessage", "additionalMessages"] }, { kind: "directive", type: MessageDirective, selector: "c8y-message", inputs: ["name", "text"] }, { kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { 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: "pipe", type: C8yTranslatePipe, name: "translate" }, { kind: "pipe", type: KeyValuePipe, name: "keyvalue" }] }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: DatapointAttributesFormComponent, decorators: [{ type: Component, args: [{ selector: 'c8y-datapoint-attributes-form', providers: [ { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => DatapointAttributesFormComponent), multi: true }, { provide: NG_VALIDATORS, useExisting: forwardRef(() => DatapointAttributesFormComponent), multi: true } ], imports: [ FormsModule, ReactiveFormsModule, C8yTranslateDirective, FormGroupComponent, RequiredInputPlaceholderDirective, MessagesComponent, MessageDirective, NgClass, NgTemplateOutlet, PopoverDirective, C8yTranslatePipe, KeyValuePipe ], template: "<div [formGroup]=\"formGroup\">\n @if (!rawValue?.__template || showFormIfTemplateWasSelected) {\n @if (formGroup.controls?.label || formGroup.controls?.unit || formGroup.controls?.target) {\n <fieldset class=\"c8y-fieldset\">\n <legend translate>Details</legend>\n <div class=\"row\">\n @if (formGroup.controls?.label) {\n <div class=\"col-md-6\">\n <c8y-form-group class=\"form-group-sm\">\n <label translate>Label</label>\n <input\n class=\"form-control\"\n name=\"label\"\n formControlName=\"label\"\n [placeholder]=\"'e.g. {{ example }}' | translate: { example: 'Temperature' }\"\n />\n <c8y-messages\n [show]=\"formGroup.controls.label.touched && formGroup.controls.label.errors\"\n >\n @for (item of customValidationErrorMessages['label'] | keyvalue; track item.key) {\n <c8y-message\n [name]=\"item.key\"\n [text]=\"item.value\"\n ></c8y-message>\n }\n </c8y-messages>\n </c8y-form-group>\n </div>\n }\n @if (formGroup.controls?.unit) {\n <div class=\"col-md-6\">\n <c8y-form-group class=\"form-group-sm\">\n <label translate>Unit</label>\n <input\n class=\"form-control\"\n name=\"unit\"\n formControlName=\"unit\"\n [placeholder]=\"'e.g. {{ example }}' | translate: { example: '\u00BAC' }\"\n />\n <c8y-messages\n [show]=\"formGroup.controls.unit.touched && formGroup.controls.unit.errors\"\n >\n @for (item of customValidationErrorMessages['unit'] | keyvalue; track item.key) {\n <c8y-message\n [name]=\"item.key\"\n [text]=\"item.value\"\n ></c8y-message>\n }\n </c8y-messages>\n </c8y-form-group>\n </div>\n }\n @if (formGroup.controls?.target && showTarget) {\n <div class=\"col-md-6\">\n <c8y-form-group\n class=\"form-group-sm\"\n [ngClass]=\"{\n 'has-error':\n (range?.touched || formGroup.controls.target.touched) &&\n formGroup.controls?.target?.errors\n }\"\n >\n <label translate>Target</label>\n <input\n class=\"form-control\"\n name=\"target\"\n type=\"number\"\n formControlName=\"target\"\n [placeholder]=\"'e.g. {{ example }}' | translate: { example: 25 }\"\n />\n <c8y-messages\n [show]=\"\n (range?.touched || formGroup.controls.target.touched) &&\n formGroup.controls.target.errors\n \"\n >\n @for (\n item of customValidationErrorMessages['target'] | keyvalue;\n track item.key\n ) {\n <c8y-message\n [name]=\"item.key\"\n [text]=\"item.value\"\n ></c8y-message>\n }\n </c8y-messages>\n </c8y-form-group>\n </div>\n }\n </div>\n </fieldset>\n }\n @if (range && showRange) {\n <fieldset class=\"c8y-fieldset\">\n <legend translate>Range</legend>\n <div\n class=\"row\"\n formGroupName=\"range\"\n >\n <div class=\"col-md-6\">\n <c8y-form-group\n class=\"form-group-sm\"\n [ngClass]=\"{ 'has-error': range?.touched && range?.controls?.min?.errors }\"\n >\n <label translate>Min</label>\n <input\n class=\"form-control\"\n name=\"min\"\n type=\"number\"\n formControlName=\"min\"\n [placeholder]=\"'e.g. {{ example }}' | translate: { example: 0 }\"\n />\n <c8y-messages [show]=\"range?.touched && range.controls?.min?.errors\"></c8y-messages>\n </c8y-form-group>\n </div>\n <div class=\"col-md-6\">\n <c8y-form-group\n class=\"form-group-sm\"\n [ngClass]=\"{ 'has-error': range?.touched && range?.controls?.max?.errors }\"\n >\n <label translate>Max</label>\n <input\n class=\"form-control\"\n name=\"max\"\n type=\"number\"\n formControlName=\"max\"\n [placeholder]=\"'e.g. {{ example }}' | translate: { example: 100 }\"\n />\n <c8y-messages [show]=\"range?.touched && range.controls?.max?.errors\">\n @for (item of customValidationErrorMessages['max'] | keyvalue; track item.key) {\n <c8y-message\n [name]=\"item.key\"\n [text]=\"item.value\"\n ></c8y-message>\n }\n </c8y-messages>\n </c8y-form-group>\n </div>\n </div>\n </fieldset>\n }\n @if (yellowRange && showYellowRange) {\n <fieldset\n class=\"c8y-fieldset\"\n formGroupName=\"yellowRange\"\n >\n <legend translate>Yellow range</legend>\n <div class=\"row\">\n <div class=\"col-md-6\">\n <c8y-form-group\n class=\"form-group-sm\"\n [ngClass]=\"{\n 'has-error':\n (range?.touched || yellowRange?.touched) && yellowRange?.controls?.min?.errors\n }\"\n >\n <label translate>Min</label>\n <input\n class=\"form-control\"\n name=\"min\"\n type=\"number\"\n formControlName=\"min\"\n [placeholder]=\"'e.g. {{ example }}' | translate: { example: 50 }\"\n />\n <c8y-messages\n [show]=\"\n (range?.touched || yellowRange?.touched) && yellowRange.controls?.min?.errors\n \"\n ></c8y-messages>\n </c8y-form-group>\n </div>\n\n <div class=\"col-md-6\">\n <c8y-form-group\n class=\"form-group-sm\"\n [ngClass]=\"{\n 'has-error':\n (range?.touched || yellowRange?.touched) && yellowRange?.controls?.max?.errors\n }\"\n >\n <label translate>Max</label>\n <input\n class=\"form-control\"\n name=\"max\"\n type=\"number\"\n formControlName=\"max\"\n [placeholder]=\"'e.g. {{ example }}' | translate: { example: 75 }\"\n />\n <c8y-messages\n [show]=\"\n (range?.touched || yellowRange?.touched) && yellowRange.controls?.max?.errors\n \"\n ></c8y-messages>\n </c8y-form-group>\n </div>\n </div>\n </fieldset>\n }\n @if (redRange && showRedRange) {\n <fieldset\n class=\"c8y-fieldset\"\n formGroupName=\"redRange\"\n >\n <legend translate>Red range</legend>\n <div class=\"row\">\n <div class=\"col-md-6\">\n <c8y-form-group\n class=\"form-group-sm\"\n [ngClass]=\"{\n 'has-error':\n (range?.touched || redRange?.touched) && redRange?.controls?.min?.errors\n }\"\n >\n <label translate>Min</label>\n <input\n class=\"form-control\"\n name=\"min\"\n type=\"number\"\n formControlName=\"min\"\n [placeholder]=\"'e.g. {{ example }}' | translate: { example: 75 }\"\n />\n <c8y-messages\n [show]=\"(range?.touched || redRange?.touched) && redRange.controls?.min?.errors\"\n ></c8y-messages>\n </c8y-form-group>\n </div>\n\n <div class=\"col-md-6\">\n <c8y-form-group\n class=\"form-group-sm\"\n [ngClass]=\"{\n 'has-error':\n (range?.touched || redRange?.touched) && redRange?.controls?.max?.errors\n }\"\n >\n <label translate>Max</label>\n <input\n class=\"form-control\"\n name=\"max\"\n type=\"number\"\n formControlName=\"max\"\n [placeholder]=\"'e.g. {{ example }}' | translate: { example: 100 }\"\n />\n <c8y-messages\n [show]=\"(range?.touched || redRange?.touched) && redRange.controls?.max?.errors\"\n ></c8y-messages>\n </c8y-form-group>\n </div>\n </div>\n </fieldset>\n }\n }\n\n @if (showChartForm) {\n <fieldset\n class=\"c8y-fieldset\"\n formGroupName=\"chart\"\n >\n <legend translate>Chart</legend>\n <div class=\"tight-grid\">\n @if (selectableChartRenderTypes?.length !== 0) {\n <div class=\"col-xs-6 col-sm-4\">\n <c8y-form-group class=\"form-group-sm\">\n <ng-container *ngTemplateOutlet=\"displayHelpButton\"></ng-container>\n <div class=\"c8y-select-wrapper\">\n <select\n class=\"form-control\"\n formControlName=\"renderType\"\n >\n @for (type of CHART_RENDER_TYPES; track type.val) {\n <option [ngValue]=\"type.val\">\n {{ type.text | translate }}\n </option>\n }\n </select>\n </div>\n </c8y-form-group>\n </div>\n }\n @if (selectableChartLineTypes?.length !== 0) {\n <div class=\"col-xs-6 col-sm-4\">\n <c8y-form-group class=\"form-group-sm\">\n <label\n for=\"chartType\"\n translate\n >\n Chart type\n </label>\n <div class=\"c8y-select-wrapper\">\n <select\n class=\"form-control\"\n formControlName=\"lineType\"\n >\n @for (type of CHART_LINE_TYPES; track type.val) {\n <option [ngValue]=\"type.val\">\n {{ type.text | translate }}\n </option>\n }\n </select>\n <span></span>\n </div>\n </c8y-form-group>\n </div>\n }\n @if (selectableAxisTypes?.length !== 0) {\n <div class=\"col-xs-6 col-sm-4\">\n <c8y-form-group class=\"form-group-sm\">\n <label\n for=\"yAxis\"\n translate\n >\n Y-axis\n </label>\n <div class=\"c8y-select-wrapper\">\n <select\n class=\"form-control\"\n formControlName=\"yAxisType\"\n >\n @for (type of AXIS_TYPES; track type.val) {\n <option [ngValue]=\"type.val\">\n {{ type.text | translate }}\n </option>\n }\n </select>\n <span></span>\n </div>\n </c8y-form-group>\n </div>\n }\n </div>\n </fieldset>\n }\n @if (showOnlyDisplayForm) {\n <fieldset\n class=\"c8y-fieldset\"\n formGroupName=\"display\"\n >\n <legend>\n <ng-container *ngTemplateOutlet=\"displayHelpButton\"></ng-container>\n </legend>\n <div class=\"tight-grid\">\n <div class=\"col-xs-6 col-sm-4\">\n <c8y-form-group class=\"form-group-sm\">\n <div class=\"c8y-select-wrapper\">\n <select\n class=\"form-control\"\n formControlName=\"renderType\"\n >\n @for (type of CHART_RENDER_TYPES; track type.val) {\n <option [ngValue]=\"type.val\">\n {{ type.text | translate }}\n </option>\n }\n </select>\n </div>\n </c8y-form-group>\n </div>\n </div>\n </fieldset>\n }\n</div>\n\n<ng-template #displayHelpButton>\n <label>\n {{ 'Display' | translate }}\n <button\n class=\"btn-help btn-help--sm\"\n [attr.aria-label]=\"'Help' | translate\"\n popover=\"{{ 'Value displayed when data is aggregated' | translate }}\"\n placement=\"right\"\n triggers=\"focus\"\n type=\"button\"\n ></button>\n </label>\n</ng-template>\n" }] }], ctorParameters: () => [{ type: DatapointAttributesFormValidationService }], propDecorators: { selectableChartRenderTypes: [{ type: Input }], selectableChartLineTypes: [{ type: Input }], selectableAxisTypes: [{ type: Input }], showTarget: [{ type: Input }], showRange: [{ type: Input }], showYellowRange: [{ type: Input }], showRedRange: [{ type: Input }], showChart: [{ type: Input }], showFormIfTemplateWasSelected: [{ type: Input }], showAdvancedChartOptions: [{ type: Input }], showErrorsImmediately: [{ type: Input }] } }); const DATAPOINT_LIBRARY_FRAGMENT = 'c8y_Kpi'; class DatapointLibraryService { constructor(inventory, appState, measurements, color) { this.inventory = inventory; this.appState = appState; this.measurements = measurements; this.color = color; this.appState.currentUser.pipe(filter(user => !user)).subscribe(() => { this.cache = undefined; }); } async getAllDatapointLibraryEntriesCached(forceCacheRenew = false) { if (forceCacheRenew) { this.cache = undefined; } if (!this.cache) { this.cache = this.getAllDatapointLibraryEntries(); } return this.cache; } async getFirstDatapointLibraryPage() { const filterObj = { currentPage: 1, pageSize: 50, fragmentType: DATAPOINT_LIBRARY_FRAGMENT, withTotalPages: true }; return (await this.inventory.list(filterObj)); } async getAllDatapointLibraryItemsCached() { if (!this.cache) { this.cache = this.getAllDatapointLibraryEntries(); } const res = await this.cache; return res.map(tmp => tmp[DATAPOINT_LIBRARY_FRAGMENT]); } async updateDatapoints(datapoints, skipUpdatingTarget = false) { if (!Array.isArray(datapoints)) { return datapoints; } const currentTargetsPromise = !skipUpdatingTarget ? this.getCurrentVersionOfTargetsFromDatapoints(datapoints) : Promise.resolve([]); const [currentTemplates, currentTargets] = await Promise.all([ this.getCurrentTemplatesFromDatapoints(datapoints), currentTargetsPromise ]); const currentTemplateVersions = currentTemplates .map(tmp => this.mapDatapointLibraryEntry(tmp)) .filter(tmp => !!tmp); for (const datapoint of datapoints) { const { fragment, series, __active, __target, color, label, __template } = datapoint; const foundCurrentTemplateVersion = currentTemplateVersions.find(tmp => tmp.__template === datapoint.__template); if (foundCurrentTemplateVersion) { Object.assign(datapoint, foundCurrentTemplateVersion); Object.assign(datapoint, { fragment, serie