import { NgTemplateOutlet, DecimalPipe, AsyncPipe } from '@angular/common';
import * as i0 from '@angular/core';
import { Injectable, inject, input, linkedSignal, signal, computed, ChangeDetectionStrategy, Component, viewChild, DestroyRef } from '@angular/core';
import { toObservable, takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { Router } from '@angular/router';
import { MeasurementService, InventoryService } from '@c8y/client';
import * as i1 from '@c8y/ngx-components';
import { AlertService, DashboardChildComponent, GroupService, DynamicComponentAlertAggregator, DynamicComponentAlert, C8yTranslateDirective, DeviceStatusComponent, DynamicComponentModule, EmptyStateComponent, ForOfDirective, GuideDocsComponent, GuideHrefDirective, IconDirective, ListGroupModule, LoadingComponent, VirtualScrollListenerDirective, WidgetActionWrapperComponent, ApplyRangeClassPipe, C8yTranslatePipe, DatePipe, FormGroupComponent } from '@c8y/ngx-components';
import { DatapointsExportSelectorComponent } from '@c8y/ngx-components/datapoints-export-selector';
import { gettext } from '@c8y/ngx-components/gettext';
import { WidgetConfigMigrationService, CONTEXT_FEATURE, GLOBAL_CONTEXT_DISPLAY_MODE, PRESET_NAME, REFRESH_OPTION, GlobalContextConnectorComponent, LocalControlsComponent } from '@c8y/ngx-components/global-context';
import { merge, isEqual } from 'lodash-es';
import { pairwise, filter, debounceTime, take, distinctUntilChanged } from 'rxjs';
import * as i1$1 from '@angular/cdk/drag-drop';
import { DragDropModule } from '@angular/cdk/drag-drop';
import * as i3 from '@angular/forms';
import { NgForm, FormBuilder, Validators, ReactiveFormsModule } from '@angular/forms';
import { WidgetConfigService } from '@c8y/ngx-components/context-dashboard';
import * as i4 from 'ngx-bootstrap/popover';
import { PopoverModule } from 'ngx-bootstrap/popover';

/**
 * Default column configuration for datapoints list widget
 */
const DEFAULT_DATAPOINTS_LIST_COLUMNS = [
    { id: 'kpi', label: gettext('Label'), visible: true, order: 0 },
    { id: 'target', label: gettext('Target'), visible: true, order: 1 },
    { id: 'current', label: gettext('Current'), visible: true, order: 2 },
    { id: 'diff', label: gettext('Diff'), visible: true, order: 3 },
    { id: 'diffPercentage', label: gettext('Diff %'), visible: true, order: 4 },
    { id: 'asset', label: gettext('Asset'), visible: true, order: 5 }
];

class DatapointsListService {
    /**
     * Calculate difference between current value and target
     * @param datapoint - Datapoint record
     * @returns Difference value or null if value/target is undefined
     */
    diff(datapoint) {
        const { currentValue, target } = datapoint;
        // != checks both null and undefined
        if (currentValue != null && target != null) {
            return currentValue - target;
        }
        return null;
    }
    /**
     * Calculate percentage difference between current value and target
     * @param datapoint - Datapoint record
     * @returns Percentage difference or null if target is undefined
     */
    diffPercent(datapoint) {
        const target = datapoint.target;
        if (target !== null && target !== undefined) {
            const _diff = this.diff(datapoint);
            if (_diff !== null) {
                // Intentionally allows division by zero to return Infinity
                // when target is 0, representing mathematically undefined percentage.
                // This follows the previous AngularJS implementation behavior.
                return (_diff / target) * 100;
            }
        }
        return null;
    }
    /**
     * Get fraction size format based on whether the value is an integer
     * @param value - Number to check
     * @param defaultFractionSize - Default fraction size format to use for non-integers
     * @returns Fraction size format ('1.0-0' for integers, defaultFractionSize for decimals)
     */
    getFractionSize(value, defaultFractionSize) {
        if (value === null || value === undefined) {
            return defaultFractionSize;
        }
        return value % 1 === 0 ? '1.0-0' : defaultFractionSize;
    }
    /**
     * Extract current value and timestamp from a measurement
     * @param datapoint - Datapoint configuration (contains fragment and series)
     * @param measurement - Measurement to extract value from
     * @returns Object containing extracted value and timestamp (null if not found)
     */
    extractMeasurementValue(datapoint, measurement) {
        // Return null values if measurement is missing
        if (!measurement) {
            return { value: null, timestamp: null };
        }
        // Return null values if datapoint configuration is incomplete
        if (!datapoint.fragment || !datapoint.series) {
            return { value: null, timestamp: null };
        }
        // Access the fragment (e.g., "c8y_Steam")
        const fragmentData = measurement[datapoint.fragment];
        if (!fragmentData) {
            return { value: null, timestamp: null };
        }
        // Access the series within the fragment (e.g., "Temperature")
        const seriesData = fragmentData[datapoint.series];
        if (!seriesData || seriesData.value === undefined) {
            return { value: null, timestamp: null };
        }
        // Extract and return the value and timestamp
        return {
            value: seriesData.value,
            timestamp: new Date(measurement.time)
        };
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: DatapointsListService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
    static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: DatapointsListService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: DatapointsListService, decorators: [{
            type: Injectable,
            args: [{
                    providedIn: 'root'
                }]
        }] });

/**
 * Service that handles all data-fetching operations for the datapoints list widget.
 * Encapsulates measurement fetching, data enrichment, and error handling.
 */
class DatapointsListFetchService {
    constructor() {
        this.alertService = inject(AlertService);
        this.measurementService = inject(MeasurementService);
        this.inventoryService = inject(InventoryService);
        this.datapointsListService = inject(DatapointsListService);
    }
    /**
     * Fetch measurements for all active datapoints and enrich them with calculated values.
     */
    async fetchDatapointsWithMeasurements(datapoints, config) {
        const targetManagedObjects = new Map();
        const results = await this.fetchAllDatapoints(datapoints, config);
        const enrichedDataPoints = results.map(r => r.datapoint);
        const seriesWithoutPermissionCount = results.filter(r => r.hasPermissionError).length;
        await this.fetchTargetManagedObjects(enrichedDataPoints, targetManagedObjects);
        return {
            dataPoints: enrichedDataPoints,
            seriesWithoutPermissionCount,
            targetManagedObjects
        };
    }
    /**
     * Fetch and enrich all datapoints in parallel.
     * Each datapoint fetch is independent - one failure doesn't affect others.
     */
    fetchAllDatapoints(datapoints, config) {
        return Promise.all(datapoints.map((datapoint, index) => this.fetchSingleDatapoint(datapoint, index, config)));
    }
    /**
     * Fetch measurement for a single datapoint and return enriched result.
     * Handles errors gracefully - returns empty datapoint on failure.
     */
    async fetchSingleDatapoint(datapoint, index, config) {
        try {
            const measurement = await this.getMeasurementForDatapoint(datapoint, config);
            const measurementValue = this.datapointsListService.extractMeasurementValue(datapoint, measurement);
            return {
                datapoint: this.createEnrichedDatapoint(datapoint, index, measurementValue, config),
                hasPermissionError: false
            };
        }
        catch (error) {
            return this.handleFetchError(error, datapoint, index, config);
        }
    }
    /**
     * Handle fetch error and return appropriate result.
     */
    handleFetchError(error, datapoint, index, config) {
        const isPermissionError = error?.status === 403;
        if (!isPermissionError) {
            this.alertService.addServerFailure(error);
        }
        return {
            datapoint: this.createEnrichedDatapoint(datapoint, index, { value: null, timestamp: null }, config),
            hasPermissionError: isPermissionError
        };
    }
    /**
     * Create an enriched datapoint with measurement value and all calculated fields.
     */
    createEnrichedDatapoint(datapoint, index, measurementValue, config) {
        const enrichedDatapoint = {
            ...datapoint,
            id: this.getDatapointId(datapoint, index),
            currentValue: measurementValue.value,
            timestamp: measurementValue.timestamp
        };
        this.calculateDerivedFields(enrichedDatapoint, config.fractionSize);
        return enrichedDatapoint;
    }
    /**
     * Calculate and set all derived fields on a datapoint.
     * Includes diff, diffPercent, and fraction sizes for display formatting.
     */
    calculateDerivedFields(datapoint, fractionSize) {
        datapoint.diffValue = this.datapointsListService.diff(datapoint);
        datapoint.diffPercentValue = this.datapointsListService.diffPercent(datapoint);
        datapoint.currentFractionSize = this.datapointsListService.getFractionSize(datapoint.currentValue, fractionSize);
        datapoint.diffFractionSize = this.datapointsListService.getFractionSize(datapoint.diffValue, fractionSize);
        datapoint.diffPercentFractionSize = this.datapointsListService.getFractionSize(datapoint.diffPercentValue, fractionSize);
    }
    /**
     * Fetch managed objects for device status display.
     */
    async fetchTargetManagedObjects(dataPoints, targetManagedObjects) {
        const uniqueTargetIds = this.getUniqueTargetIds(dataPoints);
        if (uniqueTargetIds.size === 0) {
            return;
        }
        await Promise.all(Array.from(uniqueTargetIds).map(targetId => this.fetchManagedObject(targetId, targetManagedObjects)));
    }
    /**
     * Get unique target IDs from datapoints.
     */
    getUniqueTargetIds(dataPoints) {
        const uniqueTargetIds = new Set();
        for (const dp of dataPoints) {
            if (dp.__target?.id) {
                uniqueTargetIds.add(dp.__target.id.toString());
            }
        }
        return uniqueTargetIds;
    }
    /**
     * Fetch a single managed object and add to the map.
     */
    async fetchManagedObject(targetId, targetManagedObjects) {
        try {
            const { data } = await this.inventoryService.detail(targetId);
            targetManagedObjects.set(targetId, data);
        }
        catch (error) {
            this.alertService.addServerFailure(error);
        }
    }
    /**
     * Fetch the most recent measurement for a datapoint within the specified date range.
     */
    async getMeasurementForDatapoint(datapoint, config) {
        const sourceId = datapoint.__target?.id;
        if (!sourceId || !datapoint.fragment || !datapoint.series) {
            return null;
        }
        const filter = {
            dateFrom: config.dateFrom || '1970-01-01',
            dateTo: config.dateTo || new Date().toISOString(),
            source: sourceId,
            valueFragmentSeries: datapoint.series,
            valueFragmentType: datapoint.fragment,
            pageSize: 1,
            revert: true
        };
        const { data } = await this.measurementService.list(filter);
        return data?.[0] ?? null;
    }
    /**
     * Get unique ID for a datapoint.
     * Uses target ID if available, otherwise generates fallback based on index.
     */
    getDatapointId(datapoint, fallbackIndex) {
        return datapoint.__target?.id?.toString() || `dp-${fallbackIndex}`;
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: DatapointsListFetchService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
    static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: DatapointsListFetchService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: DatapointsListFetchService, decorators: [{
            type: Injectable,
            args: [{
                    providedIn: 'root'
                }]
        }] });

class DatapointsListViewComponent {
    constructor() {
        this.config = input.required(...(ngDevMode ? [{ debugName: "config" }] : []));
        this.isInPreviewMode = input(false, ...(ngDevMode ? [{ debugName: "isInPreviewMode" }] : []));
        this.alertService = inject(AlertService);
        this.dashboardChild = inject(DashboardChildComponent, { optional: true });
        this.defaultColumns = DEFAULT_DATAPOINTS_LIST_COLUMNS;
        this.fetchService = inject(DatapointsListFetchService);
        this.groupService = inject(GroupService);
        this.inventoryService = inject(InventoryService);
        this.router = inject(Router);
        this.widgetConfigMigrationService = inject(WidgetConfigMigrationService);
        this.CONTEXT_FEATURE = CONTEXT_FEATURE;
        this.GLOBAL_CONTEXT_DISPLAY_MODE = GLOBAL_CONTEXT_DISPLAY_MODE;
        this.missingAllPermissionsAlert = new DynamicComponentAlertAggregator();
        this.targetManagedObjects = new Map();
        this.configSignal = linkedSignal(() => {
            const raw = this.config();
            const migrated = this.widgetConfigMigrationService.migrateWidgetConfig(raw);
            return merge({}, raw, migrated);
        }, ...(ngDevMode ? [{ debugName: "configSignal" }] : []));
        this.contextConfig = signal({}, ...(ngDevMode ? [{ debugName: "contextConfig" }] : []));
        this.dataPoints = signal([], ...(ngDevMode ? [{ debugName: "dataPoints" }] : []));
        this.displayMode = signal(GLOBAL_CONTEXT_DISPLAY_MODE.DASHBOARD, ...(ngDevMode ? [{ debugName: "displayMode" }] : []));
        this.hasLoadedOnce = signal(false, ...(ngDevMode ? [{ debugName: "hasLoadedOnce" }] : []));
        this.hasNoPermissionsToReadAnyMeasurement = signal(false, ...(ngDevMode ? [{ debugName: "hasNoPermissionsToReadAnyMeasurement" }] : []));
        this.isLinkedToGlobal = signal(undefined, ...(ngDevMode ? [{ debugName: "isLinkedToGlobal" }] : []));
        this.isLoading = signal(false, ...(ngDevMode ? [{ debugName: "isLoading" }] : []));
        this.widgetControls = signal(PRESET_NAME.DATA_POINTS_LIST, ...(ngDevMode ? [{ debugName: "widgetControls" }] : []));
        this.fractionSize = computed(() => {
            const decimalPlaces = this.configSignal()?.decimalPlaces;
            if (typeof decimalPlaces === 'number' && !Number.isNaN(decimalPlaces)) {
                return `1.${decimalPlaces}-${decimalPlaces}`;
            }
            return '1.2-2';
        }, ...(ngDevMode ? [{ debugName: "fractionSize" }] : []));
        this.columns = computed(() => {
            const options = this.configSignal()?.options;
            if (options?.columns?.length > 0) {
                return [...options.columns].sort((a, b) => a.order - b.order);
            }
            return this.defaultColumns.map(defaultCol => ({
                ...defaultCol,
                visible: options?.[defaultCol.id] ?? defaultCol.visible
            }));
        }, ...(ngDevMode ? [{ debugName: "columns" }] : []));
        this.visibleColumns = computed(() => {
            return this.columns().filter(col => col.visible !== false);
        }, ...(ngDevMode ? [{ debugName: "visibleColumns" }] : []));
        this.exportConfig = computed(() => {
            const effectiveConfig = { ...this.configSignal(), ...this.contextConfig() };
            const dateFrom = effectiveConfig.dateTimeContext?.dateFrom;
            const dateTo = effectiveConfig.dateTimeContext?.dateTo;
            if (!dateFrom || !dateTo || (this.isLoading() && !this.hasLoadedOnce())) {
                return null;
            }
            return {
                exportType: 'latestWithDetails',
                datapointDetails: effectiveConfig.datapoints
                    ?.filter(dp => dp.__active === true)
                    .map(dp => ({
                    deviceName: dp.__target?.name || '',
                    source: dp.__target?.id || '',
                    valueFragmentSeries: dp.series,
                    valueFragmentType: dp.fragment,
                    target: dp.target,
                    label: dp.label
                })) || [],
                dateFrom: dateFrom instanceof Date ? dateFrom.toISOString() : dateFrom,
                dateTo: dateTo instanceof Date ? dateTo.toISOString() : dateTo,
                columns: this.columns()
                    .filter(col => col.visible)
                    .map(col => ({
                    id: col.id,
                    label: col.label,
                    visible: col.visible,
                    order: col.order
                }))
            };
        }, ...(ngDevMode ? [{ debugName: "exportConfig" }] : []));
        this.activeDataPoints = computed(() => {
            return this.configSignal()?.datapoints?.filter(dp => dp.__active === true) ?? [];
        }, ...(ngDevMode ? [{ debugName: "activeDataPoints" }] : []));
        this.loadRequestId = 0;
        this.seriesWithoutPermissionToReadCount = signal(0, ...(ngDevMode ? [{ debugName: "seriesWithoutPermissionToReadCount" }] : []));
        toObservable(this.config)
            .pipe(pairwise(), takeUntilDestroyed())
            .subscribe(([prevConfig, currentConfig]) => {
            if (!this.isInPreviewMode()) {
                return;
            }
            const prevContext = this.extractContextState(prevConfig);
            const newContext = this.extractContextState(currentConfig);
            this.contextConfig.set(newContext);
            const datapointsChanged = !isEqual(prevConfig.datapoints, currentConfig.datapoints);
            const decimalPlacesChanged = prevConfig.decimalPlaces !== currentConfig.decimalPlaces;
            const contextChanged = !isEqual(prevContext, newContext);
            if (datapointsChanged || decimalPlacesChanged || contextChanged) {
                this.isLoading.set(true);
                this.loadDatapoints();
            }
        });
    }
    ngOnInit() {
        const config = this.configSignal();
        const displayMode = config.displayMode || GLOBAL_CONTEXT_DISPLAY_MODE.DASHBOARD;
        this.displayMode.set(displayMode);
        this.contextConfig.set(this.extractContextState(config));
        if (displayMode !== GLOBAL_CONTEXT_DISPLAY_MODE.DASHBOARD || this.isInPreviewMode()) {
            this.loadDatapoints();
        }
    }
    onContextChange(event) {
        const { diff, context } = event;
        this.contextConfig.set(context);
        if (diff.isAutoRefreshEnabled === false &&
            Object.keys(diff).length === 1 &&
            context.refreshOption === REFRESH_OPTION.LIVE) {
            return;
        }
        this.isLoading.set(true);
        this.loadDatapoints();
    }
    onRefresh() {
        this.isLoading.set(true);
        this.loadDatapoints();
    }
    onExportModalOpen(isOpened) {
        this.setAutoRefreshPaused(isOpened);
    }
    async redirectToAsset(assetId) {
        if (this.isInPreviewMode() || !assetId) {
            return;
        }
        try {
            const { data: mo } = await this.inventoryService.detail(assetId);
            if (mo) {
                const assetPath = this.groupService.getAssetPath(mo);
                this.router.navigateByUrl(`/${assetPath}/${mo.id}`);
            }
        }
        catch (error) {
            this.alertService.addServerFailure(error);
        }
    }
    getTargetManagedObject(targetId) {
        return this.targetManagedObjects.get(targetId.toString());
    }
    getDashboardChild() {
        return this.dashboardChild ?? null;
    }
    getRangeValues(dp) {
        return {
            yellowRangeMin: dp.yellowRangeMin,
            yellowRangeMax: dp.yellowRangeMax,
            redRangeMin: dp.redRangeMin,
            redRangeMax: dp.redRangeMax
        };
    }
    onListScrolled() {
        this.setAutoRefreshPaused(true);
    }
    onListScrolledToTop() {
        this.setAutoRefreshPaused(false);
    }
    extractContextState(config) {
        return {
            dateTimeContext: config.dateTimeContext,
            aggregation: config.aggregation,
            isAutoRefreshEnabled: config.isAutoRefreshEnabled,
            refreshInterval: config.refreshInterval,
            refreshOption: config.refreshOption
        };
    }
    setAutoRefreshPaused(paused) {
        if (this.isInPreviewMode()) {
            return;
        }
        const current = this.contextConfig();
        if (current.refreshOption === REFRESH_OPTION.HISTORY) {
            return;
        }
        this.contextConfig.set({
            ...current,
            isAutoRefreshEnabled: !paused
        });
        this.isLinkedToGlobal.set(!paused);
    }
    loadDatapoints() {
        if (this.activeDataPoints().length > 0) {
            this.fetchMeasurements();
        }
        else {
            this.dataPoints.set([]);
            this.hasLoadedOnce.set(true);
            this.isLoading.set(false);
        }
    }
    async fetchMeasurements() {
        const requestId = ++this.loadRequestId;
        try {
            this.isLoading.set(true);
            const effectiveConfig = {
                ...this.configSignal(),
                ...this.contextConfig()
            };
            const dateFrom = effectiveConfig.dateTimeContext?.dateFrom;
            const dateTo = effectiveConfig.dateTimeContext?.dateTo;
            const dateFromStr = dateFrom instanceof Date ? dateFrom.toISOString() : dateFrom;
            const dateToStr = dateTo instanceof Date ? dateTo.toISOString() : dateTo;
            const result = await this.fetchService.fetchDatapointsWithMeasurements(this.activeDataPoints(), { fractionSize: this.fractionSize(), dateFrom: dateFromStr, dateTo: dateToStr });
            if (requestId === this.loadRequestId) {
                this.dataPoints.set(result.dataPoints);
                this.targetManagedObjects = result.targetManagedObjects;
                this.hasLoadedOnce.set(true);
                this.seriesWithoutPermissionToReadCount.set(result.seriesWithoutPermissionCount);
                this.checkAndDisplayPermissionErrors();
            }
        }
        finally {
            if (requestId === this.loadRequestId) {
                this.isLoading.set(false);
            }
        }
    }
    checkAndDisplayPermissionErrors() {
        if (this.seriesWithoutPermissionToReadCount()) {
            this.missingAllPermissionsAlert.clear();
            this.handleNoPermissionErrorMessage();
        }
    }
    handleNoPermissionErrorMessage() {
        const noPermissions = this.seriesWithoutPermissionToReadCount() === this.activeDataPoints().length;
        this.hasNoPermissionsToReadAnyMeasurement.set(noPermissions);
        if (noPermissions) {
            this.showMessageForMissingPermissionsForAllSeries();
        }
    }
    showMessageForMissingPermissionsForAllSeries() {
        this.missingAllPermissionsAlert.addAlerts(new DynamicComponentAlert({
            allowHtml: true,
            text: gettext(`<p>To view data, you must meet at least one of these criteria:</p>
          <ul>
            <li>
              Have
              <b>READ permission for "Measurements" permission type</b>
              (either as a global role or for the specific source)
            </li>
            <li>
              Be the
              <b>owner of the source</b>
              you want to export data from
            </li>
          </ul>
          <p>Don't meet these requirements? Contact your system administrator for assistance.</p>`),
            type: 'system'
        }));
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: DatapointsListViewComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.19", type: DatapointsListViewComponent, isStandalone: true, selector: "c8y-datapoints-list", inputs: { config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: true, transformFunction: null }, isInPreviewMode: { classPropertyName: "isInPreviewMode", publicName: "isInPreviewMode", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "d-col fit-h" }, ngImport: i0, template: "@if (!isInPreviewMode()) {\n  <div class=\"d-flex gap-16 p-r-16 inner-scroll h-auto min-width-0\">\n    @if (displayMode() === GLOBAL_CONTEXT_DISPLAY_MODE.DASHBOARD) {\n      <c8y-global-context-connector\n        [controls]=\"widgetControls()\"\n        [config]=\"contextConfig()\"\n        [isLoading]=\"isLoading()\"\n        [dashboardChild]=\"getDashboardChild()!\"\n        [linked]=\"isLinkedToGlobal()\"\n        (configChange)=\"onContextChange($event)\"\n        (refresh)=\"onRefresh()\"\n      ></c8y-global-context-connector>\n    } @else {\n      <c8y-local-controls\n        [controls]=\"widgetControls()\"\n        [displayMode]=\"displayMode()\"\n        [config]=\"contextConfig()\"\n        [isLoading]=\"isLoading()\"\n        (configChange)=\"onContextChange($event)\"\n        (refresh)=\"onRefresh()\"\n      ></c8y-local-controls>\n    }\n  </div>\n}\n\n@if (!isInPreviewMode() && exportConfig(); as config) {\n  <c8y-widget-action>\n    <c8y-datapoints-export-selector\n      [displayMode]=\"'icon-only'\"\n      [exportConfig]=\"config\"\n      [containerClass]=\"'d-contents'\"\n      (isOpen)=\"onExportModalOpen($event)\"\n    ></c8y-datapoints-export-selector>\n  </c8y-widget-action>\n}\n\n@if (!hasNoPermissionsToReadAnyMeasurement()) {\n  <!-- the .page-sticky-header -->\n  @if (dataPoints().length > 0) {\n    <div class=\"hidden-xs hidden-sm c8y-list__item\">\n      <div class=\"c8y-list__item__block flex-grow min-width-0\">\n        <div class=\"c8y-list__item__icon\">\n          <i style=\"width: 22px\"></i>\n        </div>\n        <div class=\"c8y-list__item__body\">\n          <div class=\"d-flex-md row\">\n            @for (column of visibleColumns(); track column.id) {\n              <div\n                [class]=\"\n                  column.id === 'kpi' || column.id === 'asset'\n                    ? 'col-md-3 flex-grow min-width-0'\n                    : column.id == 'diff' || column.id == 'diffPercentage'\n                      ? 'col-md-1 flex-grow min-width-0'\n                      : 'col-md-2 flex-grow min-width-0'\n                \"\n                [class.text-right]=\"column.id !== 'asset' && column.id !== 'kpi'\"\n              >\n                <span\n                  class=\"text-medium text-truncate\"\n                  translate\n                >\n                  {{ column.label }}\n                </span>\n              </div>\n            }\n          </div>\n        </div>\n      </div>\n    </div>\n  }\n  <!-- The record list -->\n  @if (isLoading() && !hasLoadedOnce()) {\n    <!-- Initial load: full spinner -->\n    <ng-container [ngTemplateOutlet]=\"loading\"></ng-container>\n  } @else {\n    @if (isLoading()) {\n      <!-- Refresh: inline loading overlay -->\n      <div class=\"p-absolute fit-w overflow-hidden p-b-4\">\n        <c8y-loading [layout]=\"'page'\"></c8y-loading>\n      </div>\n    }\n    @if (dataPoints().length) {\n      <c8y-list-group\n        class=\"flex-grow\"\n        role=\"list\"\n        c8yVirtualScrollListener\n        (scrolled)=\"onListScrolled()\"\n        (scrolledToTop)=\"onListScrolledToTop()\"\n      >\n        <c8y-li\n          role=\"listitem\"\n          *c8yFor=\"\n            let dp of { data: dataPoints(), res: null! };\n            enableVirtualScroll: true;\n            virtualScrollElementSize: 48;\n            virtualScrollStrategy: 'fixed'\n          \"\n        >\n          <c8y-li-icon>\n            <i\n              c8yIcon=\"circle\"\n              [style.color]=\"dp.color\"\n            ></i>\n          </c8y-li-icon>\n          <c8y-li-body>\n            <div class=\"d-flex-md row\">\n              @for (column of visibleColumns(); track column.id) {\n                @switch (column.id) {\n                  @case ('kpi') {\n                    <div\n                      class=\"col-md-3 flex-grow\"\n                      [attr.data-cy]=\"'datapointlist-' + column.id\"\n                    >\n                      <div class=\"d-flex a-i-center d-contents-md p-t-4 p-b-4 separator-bottom\">\n                        <small\n                          class=\"text-label-small flex-grow visible-xs-inline-block visible-sm-inline-block\"\n                          >{{ column.label }}</small\n                        >\n                        {{ dp.label | translate }}\n                        @if (dp.unit) {\n                          <small class=\"text-muted\">{{ dp.unit }}</small>\n                        }\n                      </div>\n                    </div>\n                  }\n                  @case ('target') {\n                    <div\n                      class=\"col-md-2 text-right-md flex-grow\"\n                      [attr.data-cy]=\"'datapointlist-' + column.id\"\n                    >\n                      <div class=\"d-flex a-i-center d-contents-md p-t-4 p-b-4 separator-bottom\">\n                        <small\n                          class=\"text-label-small flex-grow visible-xs-inline-block visible-sm-inline-block\"\n                          >{{ column.label }}</small\n                        >\n                        <span>{{ dp.target }}</span>\n                      </div>\n                    </div>\n                  }\n                  @case ('current') {\n                    <div\n                      class=\"col-md-2 text-right-md flex-grow\"\n                      [attr.data-cy]=\"'datapointlist-' + column.id\"\n                    >\n                      <div class=\"d-flex a-i-center d-contents-md p-t-4 p-b-4 separator-bottom\">\n                        <small\n                          class=\"text-label-small flex-grow visible-xs-inline-block visible-sm-inline-block\"\n                          >{{ column.label }}</small\n                        >\n                        <span\n                          [class]=\"dp.currentValue | applyRangeClass: getRangeValues(dp)\"\n                          [title]=\"dp.timestamp | c8yDate: 'medium'\"\n                          [attr.aria-label]=\"\n                            ('Last updated: ' | translate) + (dp.timestamp | c8yDate: 'medium')\n                          \"\n                        >\n                          {{ dp.currentValue | number: dp.currentFractionSize }}\n                        </span>\n                      </div>\n                    </div>\n                  }\n                  @case ('diff') {\n                    <div\n                      class=\"col-md-1 text-right-md flex-grow\"\n                      [attr.data-cy]=\"'datapointlist-' + column.id\"\n                    >\n                      <div class=\"d-flex a-i-center d-contents-md p-t-4 p-b-4 separator-bottom\">\n                        <small\n                          class=\"text-label-small flex-grow visible-xs-inline-block visible-sm-inline-block\"\n                          >{{ column.label }}</small\n                        >\n                        <span>\n                          {{ dp.diffValue | number: dp.diffFractionSize }}\n                        </span>\n                      </div>\n                    </div>\n                  }\n                  @case ('diffPercentage') {\n                    <div\n                      class=\"col-md-1 text-right-md flex-grow\"\n                      [attr.data-cy]=\"'datapointlist-' + column.id\"\n                    >\n                      <div class=\"d-flex a-i-center d-contents-md p-t-4 p-b-4 separator-bottom\">\n                        <small\n                          class=\"text-label-small flex-grow visible-xs-inline-block visible-sm-inline-block\"\n                          >{{ column.label }}</small\n                        >\n                        <span>\n                          {{ dp.diffPercentValue | number: dp.diffPercentFractionSize }}\n                        </span>\n                      </div>\n                    </div>\n                  }\n                  @case ('asset') {\n                    <div\n                      class=\"col-md-3 flex-grow\"\n                      [attr.data-cy]=\"'datapointlist-' + column.id\"\n                    >\n                      <div class=\"d-flex a-i-center d-contents-md p-t-4 p-b-4 separator-bottom\">\n                        <small\n                          class=\"text-label-small flex-grow visible-xs-inline-block visible-sm-inline-block\"\n                          >{{ column.label }}</small\n                        >\n                        <button\n                          class=\"btn-clean d-flex a-i-center gap-4 text-muted\"\n                          [attr.aria-label]=\"('Navigate to ' | translate) + dp.__target?.name\"\n                          type=\"button\"\n                          (click)=\"redirectToAsset(dp.__target?.id)\"\n                        >\n                          @if (dp.__target?.id && getTargetManagedObject(dp.__target.id); as mo) {\n                            <c8y-device-status\n                              [mo]=\"mo\"\n                              [size]=\"16\"\n                            ></c8y-device-status>\n                          }\n                          <small\n                            class=\"text-truncate\"\n                            [title]=\"dp.__target?.name\"\n                            >{{ dp.__target?.name }}</small\n                          >\n                        </button>\n                      </div>\n                    </div>\n                  }\n                }\n              }\n            </div>\n          </c8y-li-body>\n        </c8y-li>\n      </c8y-list-group>\n    } @else {\n      <ng-container [ngTemplateOutlet]=\"emptyState\"></ng-container>\n    }\n  }\n} @else {\n  <div class=\"p-t-24 p-r-16 p-l-16 p-b-16 d-flex\">\n    <div class=\"center-block\">\n      <c8y-dynamic-component-alerts\n        [alerts]=\"missingAllPermissionsAlert\"\n      ></c8y-dynamic-component-alerts>\n    </div>\n  </div>\n}\n\n<ng-template #loading>\n  <c8y-loading></c8y-loading>\n</ng-template>\n\n<ng-template #emptyState>\n  <div class=\"p-relative p-l-24\">\n    <c8y-ui-empty-state\n      [icon]=\"'c8y-alert-idle'\"\n      [title]=\"'No data to display.' | translate\"\n      [horizontal]=\"true\"\n      data-cy=\"datapoints-list--empty-state\"\n    >\n      <p c8y-guide-docs>\n        <small translate>\n          Find out more in the\n          <a c8y-guide-href=\"/docs/cockpit/widgets-collection/#data-point-list\">\n            user documentation\n          </a>\n          .\n        </small>\n      </p>\n    </c8y-ui-empty-state>\n  </div>\n</ng-template>\n", dependencies: [{ kind: "directive", type: C8yTranslateDirective, selector: "[translate],[ngx-translate]" }, { kind: "component", type: DatapointsExportSelectorComponent, selector: "c8y-datapoints-export-selector", inputs: ["displayMode", "containerClass", "exportConfig"], outputs: ["isOpen"] }, { kind: "component", type: DeviceStatusComponent, selector: "device-status, c8y-device-status", inputs: ["mo", "size"] }, { kind: "ngmodule", type: DynamicComponentModule }, { kind: "component", type: i1.DynamicComponentAlertsComponent, selector: "c8y-dynamic-component-alerts", inputs: ["alerts"] }, { kind: "component", type: EmptyStateComponent, selector: "c8y-ui-empty-state", inputs: ["icon", "title", "subtitle", "horizontal"] }, { kind: "directive", type: ForOfDirective, selector: "[c8yFor]", inputs: ["c8yForOf", "c8yForLoadMore", "c8yForPipe", "c8yForNotFound", "c8yForMaxIterations", "c8yForLoadingTemplate", "c8yForLoadNextLabel", "c8yForLoadingLabel", "c8yForRealtime", "c8yForRealtimeOptions", "c8yForComparator", "c8yForEnableVirtualScroll", "c8yForVirtualScrollElementSize", "c8yForVirtualScrollStrategy", "c8yForVirtualScrollContainerHeight"], outputs: ["c8yForCount", "c8yForChange", "c8yForLoadMoreComponent"] }, { kind: "component", type: GlobalContextConnectorComponent, selector: "c8y-global-context-connector", inputs: ["controls", "config", "isLoading", "dashboardChild", "linked", "emitRefresh"], outputs: ["configChange", "refresh", "linkedChange"] }, { kind: "component", type: GuideDocsComponent, selector: "[c8y-guide-docs]" }, { kind: "directive", type: GuideHrefDirective, selector: "[c8y-guide-href]", inputs: ["c8y-guide-href"] }, { kind: "directive", type: IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "ngmodule", type: ListGroupModule }, { kind: "component", type: i1.ListGroupComponent, selector: "c8y-list-group" }, { kind: "component", type: i1.ListItemComponent, selector: "c8y-list-item, c8y-li", inputs: ["active", "highlighted", "emptyActions", "dense", "collapsed", "selectable"], outputs: ["collapsedChange"] }, { kind: "component", type: i1.ListItemIconComponent, selector: "c8y-list-item-icon, c8y-li-icon", inputs: ["icon", "status"] }, { kind: "component", type: i1.ListItemBodyComponent, selector: "c8y-list-item-body, c8y-li-body", inputs: ["body"] }, { kind: "component", type: LoadingComponent, selector: "c8y-loading", inputs: ["layout", "progress", "message"] }, { kind: "component", type: LocalControlsComponent, selector: "c8y-local-controls", inputs: ["controls", "displayMode", "config", "isLoading", "disabled", "emitRefresh"], outputs: ["configChange", "refresh"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: VirtualScrollListenerDirective, selector: "[c8yVirtualScrollListener]", inputs: ["scrollThreshold"], outputs: ["scrolled", "scrolledToTop"] }, { kind: "component", type: WidgetActionWrapperComponent, selector: "c8y-widget-action" }, { kind: "pipe", type: ApplyRangeClassPipe, name: "applyRangeClass" }, { kind: "pipe", type: C8yTranslatePipe, name: "translate" }, { kind: "pipe", type: DatePipe, name: "c8yDate" }, { kind: "pipe", type: DecimalPipe, name: "number" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: DatapointsListViewComponent, decorators: [{
            type: Component,
            args: [{ selector: 'c8y-datapoints-list', host: { class: 'd-col fit-h' }, changeDetection: ChangeDetectionStrategy.OnPush, imports: [
                        ApplyRangeClassPipe,
                        C8yTranslateDirective,
                        C8yTranslatePipe,
                        DatapointsExportSelectorComponent,
                        DatePipe,
                        DecimalPipe,
                        DeviceStatusComponent,
                        DynamicComponentModule,
                        EmptyStateComponent,
                        ForOfDirective,
                        GlobalContextConnectorComponent,
                        GuideDocsComponent,
                        GuideHrefDirective,
                        IconDirective,
                        ListGroupModule,
                        LoadingComponent,
                        LocalControlsComponent,
                        NgTemplateOutlet,
                        VirtualScrollListenerDirective,
                        WidgetActionWrapperComponent
                    ], template: "@if (!isInPreviewMode()) {\n  <div class=\"d-flex gap-16 p-r-16 inner-scroll h-auto min-width-0\">\n    @if (displayMode() === GLOBAL_CONTEXT_DISPLAY_MODE.DASHBOARD) {\n      <c8y-global-context-connector\n        [controls]=\"widgetControls()\"\n        [config]=\"contextConfig()\"\n        [isLoading]=\"isLoading()\"\n        [dashboardChild]=\"getDashboardChild()!\"\n        [linked]=\"isLinkedToGlobal()\"\n        (configChange)=\"onContextChange($event)\"\n        (refresh)=\"onRefresh()\"\n      ></c8y-global-context-connector>\n    } @else {\n      <c8y-local-controls\n        [controls]=\"widgetControls()\"\n        [displayMode]=\"displayMode()\"\n        [config]=\"contextConfig()\"\n        [isLoading]=\"isLoading()\"\n        (configChange)=\"onContextChange($event)\"\n        (refresh)=\"onRefresh()\"\n      ></c8y-local-controls>\n    }\n  </div>\n}\n\n@if (!isInPreviewMode() && exportConfig(); as config) {\n  <c8y-widget-action>\n    <c8y-datapoints-export-selector\n      [displayMode]=\"'icon-only'\"\n      [exportConfig]=\"config\"\n      [containerClass]=\"'d-contents'\"\n      (isOpen)=\"onExportModalOpen($event)\"\n    ></c8y-datapoints-export-selector>\n  </c8y-widget-action>\n}\n\n@if (!hasNoPermissionsToReadAnyMeasurement()) {\n  <!-- the .page-sticky-header -->\n  @if (dataPoints().length > 0) {\n    <div class=\"hidden-xs hidden-sm c8y-list__item\">\n      <div class=\"c8y-list__item__block flex-grow min-width-0\">\n        <div class=\"c8y-list__item__icon\">\n          <i style=\"width: 22px\"></i>\n        </div>\n        <div class=\"c8y-list__item__body\">\n          <div class=\"d-flex-md row\">\n            @for (column of visibleColumns(); track column.id) {\n              <div\n                [class]=\"\n                  column.id === 'kpi' || column.id === 'asset'\n                    ? 'col-md-3 flex-grow min-width-0'\n                    : column.id == 'diff' || column.id == 'diffPercentage'\n                      ? 'col-md-1 flex-grow min-width-0'\n                      : 'col-md-2 flex-grow min-width-0'\n                \"\n                [class.text-right]=\"column.id !== 'asset' && column.id !== 'kpi'\"\n              >\n                <span\n                  class=\"text-medium text-truncate\"\n                  translate\n                >\n                  {{ column.label }}\n                </span>\n              </div>\n            }\n          </div>\n        </div>\n      </div>\n    </div>\n  }\n  <!-- The record list -->\n  @if (isLoading() && !hasLoadedOnce()) {\n    <!-- Initial load: full spinner -->\n    <ng-container [ngTemplateOutlet]=\"loading\"></ng-container>\n  } @else {\n    @if (isLoading()) {\n      <!-- Refresh: inline loading overlay -->\n      <div class=\"p-absolute fit-w overflow-hidden p-b-4\">\n        <c8y-loading [layout]=\"'page'\"></c8y-loading>\n      </div>\n    }\n    @if (dataPoints().length) {\n      <c8y-list-group\n        class=\"flex-grow\"\n        role=\"list\"\n        c8yVirtualScrollListener\n        (scrolled)=\"onListScrolled()\"\n        (scrolledToTop)=\"onListScrolledToTop()\"\n      >\n        <c8y-li\n          role=\"listitem\"\n          *c8yFor=\"\n            let dp of { data: dataPoints(), res: null! };\n            enableVirtualScroll: true;\n            virtualScrollElementSize: 48;\n            virtualScrollStrategy: 'fixed'\n          \"\n        >\n          <c8y-li-icon>\n            <i\n              c8yIcon=\"circle\"\n              [style.color]=\"dp.color\"\n            ></i>\n          </c8y-li-icon>\n          <c8y-li-body>\n            <div class=\"d-flex-md row\">\n              @for (column of visibleColumns(); track column.id) {\n                @switch (column.id) {\n                  @case ('kpi') {\n                    <div\n                      class=\"col-md-3 flex-grow\"\n                      [attr.data-cy]=\"'datapointlist-' + column.id\"\n                    >\n                      <div class=\"d-flex a-i-center d-contents-md p-t-4 p-b-4 separator-bottom\">\n                        <small\n                          class=\"text-label-small flex-grow visible-xs-inline-block visible-sm-inline-block\"\n                          >{{ column.label }}</small\n                        >\n                        {{ dp.label | translate }}\n                        @if (dp.unit) {\n                          <small class=\"text-muted\">{{ dp.unit }}</small>\n                        }\n                      </div>\n                    </div>\n                  }\n                  @case ('target') {\n                    <div\n                      class=\"col-md-2 text-right-md flex-grow\"\n                      [attr.data-cy]=\"'datapointlist-' + column.id\"\n                    >\n                      <div class=\"d-flex a-i-center d-contents-md p-t-4 p-b-4 separator-bottom\">\n                        <small\n                          class=\"text-label-small flex-grow visible-xs-inline-block visible-sm-inline-block\"\n                          >{{ column.label }}</small\n                        >\n                        <span>{{ dp.target }}</span>\n                      </div>\n                    </div>\n                  }\n                  @case ('current') {\n                    <div\n                      class=\"col-md-2 text-right-md flex-grow\"\n                      [attr.data-cy]=\"'datapointlist-' + column.id\"\n                    >\n                      <div class=\"d-flex a-i-center d-contents-md p-t-4 p-b-4 separator-bottom\">\n                        <small\n                          class=\"text-label-small flex-grow visible-xs-inline-block visible-sm-inline-block\"\n                          >{{ column.label }}</small\n                        >\n                        <span\n                          [class]=\"dp.currentValue | applyRangeClass: getRangeValues(dp)\"\n                          [title]=\"dp.timestamp | c8yDate: 'medium'\"\n                          [attr.aria-label]=\"\n                            ('Last updated: ' | translate) + (dp.timestamp | c8yDate: 'medium')\n                          \"\n                        >\n                          {{ dp.currentValue | number: dp.currentFractionSize }}\n                        </span>\n                      </div>\n                    </div>\n                  }\n                  @case ('diff') {\n                    <div\n                      class=\"col-md-1 text-right-md flex-grow\"\n                      [attr.data-cy]=\"'datapointlist-' + column.id\"\n                    >\n                      <div class=\"d-flex a-i-center d-contents-md p-t-4 p-b-4 separator-bottom\">\n                        <small\n                          class=\"text-label-small flex-grow visible-xs-inline-block visible-sm-inline-block\"\n                          >{{ column.label }}</small\n                        >\n                        <span>\n                          {{ dp.diffValue | number: dp.diffFractionSize }}\n                        </span>\n                      </div>\n                    </div>\n                  }\n                  @case ('diffPercentage') {\n                    <div\n                      class=\"col-md-1 text-right-md flex-grow\"\n                      [attr.data-cy]=\"'datapointlist-' + column.id\"\n                    >\n                      <div class=\"d-flex a-i-center d-contents-md p-t-4 p-b-4 separator-bottom\">\n                        <small\n                          class=\"text-label-small flex-grow visible-xs-inline-block visible-sm-inline-block\"\n                          >{{ column.label }}</small\n                        >\n                        <span>\n                          {{ dp.diffPercentValue | number: dp.diffPercentFractionSize }}\n                        </span>\n                      </div>\n                    </div>\n                  }\n                  @case ('asset') {\n                    <div\n                      class=\"col-md-3 flex-grow\"\n                      [attr.data-cy]=\"'datapointlist-' + column.id\"\n                    >\n                      <div class=\"d-flex a-i-center d-contents-md p-t-4 p-b-4 separator-bottom\">\n                        <small\n                          class=\"text-label-small flex-grow visible-xs-inline-block visible-sm-inline-block\"\n                          >{{ column.label }}</small\n                        >\n                        <button\n                          class=\"btn-clean d-flex a-i-center gap-4 text-muted\"\n                          [attr.aria-label]=\"('Navigate to ' | translate) + dp.__target?.name\"\n                          type=\"button\"\n                          (click)=\"redirectToAsset(dp.__target?.id)\"\n                        >\n                          @if (dp.__target?.id && getTargetManagedObject(dp.__target.id); as mo) {\n                            <c8y-device-status\n                              [mo]=\"mo\"\n                              [size]=\"16\"\n                            ></c8y-device-status>\n                          }\n                          <small\n                            class=\"text-truncate\"\n                            [title]=\"dp.__target?.name\"\n                            >{{ dp.__target?.name }}</small\n                          >\n                        </button>\n                      </div>\n                    </div>\n                  }\n                }\n              }\n            </div>\n          </c8y-li-body>\n        </c8y-li>\n      </c8y-list-group>\n    } @else {\n      <ng-container [ngTemplateOutlet]=\"emptyState\"></ng-container>\n    }\n  }\n} @else {\n  <div class=\"p-t-24 p-r-16 p-l-16 p-b-16 d-flex\">\n    <div class=\"center-block\">\n      <c8y-dynamic-component-alerts\n        [alerts]=\"missingAllPermissionsAlert\"\n      ></c8y-dynamic-component-alerts>\n    </div>\n  </div>\n}\n\n<ng-template #loading>\n  <c8y-loading></c8y-loading>\n</ng-template>\n\n<ng-template #emptyState>\n  <div class=\"p-relative p-l-24\">\n    <c8y-ui-empty-state\n      [icon]=\"'c8y-alert-idle'\"\n      [title]=\"'No data to display.' | translate\"\n      [horizontal]=\"true\"\n      data-cy=\"datapoints-list--empty-state\"\n    >\n      <p c8y-guide-docs>\n        <small translate>\n          Find out more in the\n          <a c8y-guide-href=\"/docs/cockpit/widgets-collection/#data-point-list\">\n            user documentation\n          </a>\n          .\n        </small>\n      </p>\n    </c8y-ui-empty-state>\n  </div>\n</ng-template>\n" }]
        }], ctorParameters: () => [], propDecorators: { config: [{ type: i0.Input, args: [{ isSignal: true, alias: "config", required: true }] }], isInPreviewMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "isInPreviewMode", required: false }] }] } });

const DEFAULT_DECIMAL_PLACES = 2;
const MIN_DECIMAL_PLACES = 0;
const MAX_DECIMAL_PLACES = 10;
class DatapointsListConfigComponent {
    constructor() {
        this.config = input.required(...(ngDevMode ? [{ debugName: "config" }] : []));
        this.previewTemplate = viewChild('dataPointsListPreview', ...(ngDevMode ? [{ debugName: "previewTemplate" }] : []));
        this.defaultColumns = DEFAULT_DATAPOINTS_LIST_COLUMNS;
        this.destroyRef = inject(DestroyRef);
        this.form = inject(NgForm);
        this.formBuilder = inject(FormBuilder);
        this.widgetConfigService = inject(WidgetConfigService);
        this.controls = PRESET_NAME.DATA_POINTS_LIST;
        this.minDecimalPlaces = MIN_DECIMAL_PLACES;
        this.maxDecimalPlaces = MAX_DECIMAL_PLACES;
        this.configForm = signal(undefined, ...(ngDevMode ? [{ debugName: "configForm" }] : []));
        this.columnsFormArray = computed(() => this.configForm()?.get('columns'), ...(ngDevMode ? [{ debugName: "columnsFormArray" }] : []));
        /**
         * Debounced config for preview to prevent multiple series requests on initial load.
         * Uses debounceTime to batch rapid emissions (e.g., from initConfig + GlobalContext processing).
         */
        this.previewConfig$ = this.widgetConfigService.currentConfig$.pipe(filter(config => !!config?.dateTimeContext), debounceTime(300));
    }
    ngOnInit() {
        this.widgetConfigService.currentConfig$
            .pipe(filter(config => !!config?.dateTimeContext), take(1), takeUntilDestroyed(this.destroyRef))
            .subscribe(() => this.initForm());
    }
    ngAfterViewInit() {
        this.widgetConfigService.setPreview(this.previewTemplate() ?? null);
    }
    onColumnDrop(event) {
        const columnsArray = this.columnsFormArray();
        const item = columnsArray.at(event.previousIndex);
        columnsArray.removeAt(event.previousIndex);
        columnsArray.insert(event.currentIndex, item);
        columnsArray.controls.forEach((control, index) => control.patchValue({ order: index }));
    }
    initForm() {
        const form = this.createForm();
        this.form.form.addControl('config', form);
        form.patchValue(this.config(), { emitEvent: false });
        this.pushFormToService(form.value);
        form.valueChanges
            .pipe(distinctUntilChanged((prev, curr) => isEqual(prev, curr)), debounceTime(300), takeUntilDestroyed(this.destroyRef))
            .subscribe(formValue => {
            if (form.invalid) {
                return;
            }
            const { decimalPlaces } = formValue;
            if (typeof decimalPlaces === 'number' &&
                (decimalPlaces < MIN_DECIMAL_PLACES || decimalPlaces > MAX_DECIMAL_PLACES)) {
                return;
            }
            this.pushFormToService(formValue);
        });
        this.configForm.set(form);
    }
    pushFormToService(formValue) {
        const formData = formValue;
        const { columns, ...formValueWithoutColumns } = formData;
        const currentOptions = this.widgetConfigService.currentConfig?.options;
        this.widgetConfigService.updateConfig({
            ...formValueWithoutColumns,
            options: {
                ...currentOptions,
                columns: columns || []
            }
        });
    }
    createForm() {
        const currentConfig = this.config();
        const columns = this.migrateColumnsConfig(currentConfig);
        return this.formBuilder.group({
            decimalPlaces: [
                currentConfig?.decimalPlaces ?? DEFAULT_DECIMAL_PLACES,
                [
                    Validators.required,
                    Validators.min(MIN_DECIMAL_PLACES),
                    Validators.max(MAX_DECIMAL_PLACES),
                    Validators.pattern('^[0-9]+$')
                ]
            ],
            columns: this.formBuilder.array(columns.map(col => this.createColumnFormGroup(col)), [Validators.required, Validators.minLength(1), this.minOneColumnVisible()])
        });
    }
    createColumnFormGroup(column) {
        return this.formBuilder.group({
            id: [column.id, Validators.required],
            label: [column.label, Validators.required],
            visible: [column.visible],
            order: [column.order]
        });
    }
    migrateColumnsConfig(config) {
        if (config?.options?.columns?.length > 0) {
            return [...config.options.columns].sort((a, b) => a.order - b.order);
        }
        return this.defaultColumns.map(defaultCol => ({
            ...defaultCol,
            visible: config?.options?.[defaultCol.id] ?? defaultCol.visible
        }));
    }
    minOneColumnVisible() {
        return (control) => {
            const columns = control.value;
            if (!columns?.length) {
                return null;
            }
            const visibleColumns = columns.filter(column => column.visible);
            return visibleColumns.length >= 1 ? null : { atLeastOneColumnMustBeVisible: true };
        };
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: DatapointsListConfigComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.19", type: DatapointsListConfigComponent, isStandalone: true, selector: "c8y-datapoints-list-view-config", inputs: { config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: true, transformFunction: null } }, viewQueries: [{ propertyName: "previewTemplate", first: true, predicate: ["dataPointsListPreview"], descendants: true, isSignal: true }], ngImport: i0, template: "@if (configForm(); as form) {\n  <form\n    class=\"no-card-context\"\n    [formGroup]=\"form\"\n  >\n    <fieldset class=\"c8y-fieldset\">\n      <legend>{{ 'Columns (drag to reorder)' | translate }}</legend>\n      <c8y-list-group\n        formArrayName=\"columns\"\n        cdkDropList\n        (cdkDropListDropped)=\"onColumnDrop($event)\"\n      >\n        @if (columnsFormArray().errors?.atLeastOneColumnMustBeVisible) {\n          <div\n            class=\"alert alert-warning m-t-8\"\n            role=\"alert\"\n          >\n            {{ 'At least 1 column must be visible.' | translate }}\n          </div>\n        }\n\n        @for (column of columnsFormArray().controls; track column.value.id; let i = $index) {\n          <c8y-li\n            class=\"c8y-list__item__collapse--container-small\"\n            [formGroupName]=\"i\"\n            cdkDrag\n          >\n            <c8y-li-drag-handle\n              [title]=\"'Click and drag to reorder' | translate\"\n              cdkDragHandle\n            >\n              <i c8yIcon=\"drag-reorder\"></i>\n            </c8y-li-drag-handle>\n            <c8y-li-checkbox\n              class=\"a-s-center p-r-0\"\n              [displayAsSwitch]=\"true\"\n              formControlName=\"visible\"\n              (click)=\"$event.stopPropagation()\"\n            ></c8y-li-checkbox>\n            <c8y-li-body>\n              <div class=\"d-flex a-i-center\">\n                <span class=\"text-truncate\">{{ column.value.label | translate }}</span>\n                @switch (column.value.label) {\n                  @case ('Target') {\n                    <button\n                      class=\"btn-help\"\n                      [attr.aria-label]=\"'Help content' | translate\"\n                      [popover]=\"\n                        'The Target column shows the value set on the target field of the data point'\n                          | translate\n                      \"\n                      placement=\"right\"\n                      triggers=\"focus\"\n                      container=\"body\"\n                      type=\"button\"\n                    ></button>\n                  }\n                  @case ('Diff') {\n                    <button\n                      class=\"btn-help\"\n                      [attr.aria-label]=\"'Help content' | translate\"\n                      [popover]=\"\n                        'The Diff column shows the difference between the current value and the target value of the data point'\n                          | translate\n                      \"\n                      placement=\"right\"\n                      triggers=\"focus\"\n                      container=\"body\"\n                      type=\"button\"\n                    ></button>\n                  }\n                }\n              </div>\n            </c8y-li-body>\n          </c8y-li>\n        }\n      </c8y-list-group>\n    </fieldset>\n\n    <!-- decimal input -->\n    <fieldset class=\"c8y-fieldset\">\n      <legend>\n        {{ 'Decimal places' | translate }}\n      </legend>\n      <c8y-form-group class=\"p-t-8\">\n        <input\n          class=\"form-control\"\n          name=\"decimalPlaces\"\n          type=\"number\"\n          formControlName=\"decimalPlaces\"\n          step=\"1\"\n          [min]=\"minDecimalPlaces\"\n          [max]=\"maxDecimalPlaces\"\n        />\n      </c8y-form-group>\n    </fieldset>\n  </form>\n}\n\n<ng-template #dataPointsListPreview>\n  @let previewConfig = previewConfig$ | async;\n  @if (previewConfig && previewConfig.displayMode !== 'dashboard') {\n    <c8y-local-controls\n      [controls]=\"controls\"\n      [displayMode]=\"previewConfig.displayMode!\"\n      [config]=\"previewConfig\"\n      [disabled]=\"true\"\n    ></c8y-local-controls>\n  }\n\n  @if (previewConfig) {\n    <c8y-datapoints-list\n      [config]=\"previewConfig\"\n      [isInPreviewMode]=\"true\"\n      data-cy=\"c8y-datapoints-list-widget-config--preview-datapoints-list\"\n    ></c8y-datapoints-list>\n  }\n</ng-template>\n", dependencies: [{ kind: "component", type: DatapointsListViewComponent, selector: "c8y-datapoints-list", inputs: ["config", "isInPreviewMode"] }, { kind: "ngmodule", type: DragDropModule }, { kind: "directive", type: i1$1.CdkDropList, selector: "[cdkDropList], cdk-drop-list", inputs: ["cdkDropListConnectedTo", "cdkDropListData", "cdkDropListOrientation", "id", "cdkDropListLockAxis", "cdkDropListDisabled", "cdkDropListSortingDisabled", "cdkDropListEnterPredicate", "cdkDropListSortPredicate", "cdkDropListAutoScrollDisabled", "cdkDropListAutoScrollStep", "cdkDropListElementContainer", "cdkDropListHasAnchor"], outputs: ["cdkDropListDropped", "cdkDropListEntered", "cdkDropListExited", "cdkDropListSorted"], exportAs: ["cdkDropList"] }, { kind: "directive", type: i1$1.CdkDrag, selector: "[cdkDrag]", inputs: ["cdkDragData", "cdkDragLockAxis", "cdkDragRootElement", "cdkDragBoundary", "cdkDragStartDelay", "cdkDragFreeDragPosition", "cdkDragDisabled", "cdkDragConstrainPosition", "cdkDragPreviewClass", "cdkDragPreviewContainer", "cdkDragScale"], outputs: ["cdkDragStarted", "cdkDragReleased", "cdkDragEnded", "cdkDragEntered", "cdkDragExited", "cdkDragDropped", "cdkDragMoved"], exportAs: ["cdkDrag"] }, { kind: "directive", type: i1$1.CdkDragHandle, selector: "[cdkDragHandle]", inputs: ["cdkDragHandleDisabled"] }, { kind: "ngmodule", type: DynamicComponentModule }, { kind: "component", type: FormGroupComponent, selector: "c8y-form-group", inputs: ["hasError", "hasWarning", "hasSuccess", "novalidation", "status"] }, { kind: "directive", type: IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "ngmodule", type: ListGroupModule }, { kind: "component", type: i1.ListGroupComponent, selector: "c8y-list-group" }, { kind: "component", type: i1.ListItemComponent, selector: "c8y-list-item, c8y-li", inputs: ["active", "highlighted", "emptyActions", "dense", "collapsed", "selectable"], outputs: ["collapsedChange"] }, { kind: "component", type: i1.ListItemBodyComponent, selector: "c8y-list-item-body, c8y-li-body", inputs: ["body"] }, { kind: "component", type: i1.ListItemCheckboxComponent, selector: "c8y-list-item-checkbox, c8y-li-checkbox", inputs: ["selected", "indeterminate", "disabled", "displayAsSwitch"], outputs: ["onSelect"] }, { kind: "component", type: i1.ListItemDragHandleComponent, selector: "c8y-list-item-drag-handle, c8y-li-drag-handle" }, { kind: "component", type: LocalControlsComponent, selector: "c8y-local-controls", inputs: ["controls", "displayMode", "config", "isLoading", "disabled", "emitRefresh"], outputs: ["configChange", "refresh"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i3.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i3.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: i3.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i3.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i3.MinValidator, selector: "input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]", inputs: ["min"] }, { kind: "directive", type: i3.MaxValidator, selector: "input[type=number][max][formControlName],input[type=number][max][formControl],input[type=number][max][ngModel]", inputs: ["max"] }, { kind: "directive", type: i3.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i3.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "directive", type: i3.FormGroupName, selector: "[formGroupName]", inputs: ["formGroupName"] }, { kind: "directive", type: i3.FormArrayName, selector: "[formArrayName]", inputs: ["formArrayName"] }, { kind: "ngmodule", type: PopoverModule }, { 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: "pipe", type: AsyncPipe, name: "async" }, { kind: "pipe", type: C8yTranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: DatapointsListConfigComponent, decorators: [{
            type: Component,
            args: [{ selector: 'c8y-datapoints-list-view-config', changeDetection: ChangeDetectionStrategy.OnPush, imports: [
                        AsyncPipe,
                        C8yTranslatePipe,
                        DatapointsListViewComponent,
                        DragDropModule,
                        DynamicComponentModule,
                        FormGroupComponent,
                        IconDirective,
                        ListGroupModule,
                        LocalControlsComponent,
                        ReactiveFormsModule,
                        PopoverModule
                    ], template: "@if (configForm(); as form) {\n  <form\n    class=\"no-card-context\"\n    [formGroup]=\"form\"\n  >\n    <fieldset class=\"c8y-fieldset\">\n      <legend>{{ 'Columns (drag to reorder)' | translate }}</legend>\n      <c8y-list-group\n        formArrayName=\"columns\"\n        cdkDropList\n        (cdkDropListDropped)=\"onColumnDrop($event)\"\n      >\n        @if (columnsFormArray().errors?.atLeastOneColumnMustBeVisible) {\n          <div\n            class=\"alert alert-warning m-t-8\"\n            role=\"alert\"\n          >\n            {{ 'At least 1 column must be visible.' | translate }}\n          </div>\n        }\n\n        @for (column of columnsFormArray().controls; track column.value.id; let i = $index) {\n          <c8y-li\n            class=\"c8y-list__item__collapse--container-small\"\n            [formGroupName]=\"i\"\n            cdkDrag\n          >\n            <c8y-li-drag-handle\n              [title]=\"'Click and drag to reorder' | translate\"\n              cdkDragHandle\n            >\n              <i c8yIcon=\"drag-reorder\"></i>\n            </c8y-li-drag-handle>\n            <c8y-li-checkbox\n              class=\"a-s-center p-r-0\"\n              [displayAsSwitch]=\"true\"\n              formControlName=\"visible\"\n              (click)=\"$event.stopPropagation()\"\n            ></c8y-li-checkbox>\n            <c8y-li-body>\n              <div class=\"d-flex a-i-center\">\n                <span class=\"text-truncate\">{{ column.value.label | translate }}</span>\n                @switch (column.value.label) {\n                  @case ('Target') {\n                    <button\n                      class=\"btn-help\"\n                      [attr.aria-label]=\"'Help content' | translate\"\n                      [popover]=\"\n                        'The Target column shows the value set on the target field of the data point'\n                          | translate\n                      \"\n                      placement=\"right\"\n                      triggers=\"focus\"\n                      container=\"body\"\n                      type=\"button\"\n                    ></button>\n                  }\n                  @case ('Diff') {\n                    <button\n                      class=\"btn-help\"\n                      [attr.aria-label]=\"'Help content' | translate\"\n                      [popover]=\"\n                        'The Diff column shows the difference between the current value and the target value of the data point'\n                          | translate\n                      \"\n                      placement=\"right\"\n                      triggers=\"focus\"\n                      container=\"body\"\n                      type=\"button\"\n                    ></button>\n                  }\n                }\n              </div>\n            </c8y-li-body>\n          </c8y-li>\n        }\n      </c8y-list-group>\n    </fieldset>\n\n    <!-- decimal input -->\n    <fieldset class=\"c8y-fieldset\">\n      <legend>\n        {{ 'Decimal places' | translate }}\n      </legend>\n      <c8y-form-group class=\"p-t-8\">\n        <input\n          class=\"form-control\"\n          name=\"decimalPlaces\"\n          type=\"number\"\n          formControlName=\"decimalPlaces\"\n          step=\"1\"\n          [min]=\"minDecimalPlaces\"\n          [max]=\"maxDecimalPlaces\"\n        />\n      </c8y-form-group>\n    </fieldset>\n  </form>\n}\n\n<ng-template #dataPointsListPreview>\n  @let previewConfig = previewConfig$ | async;\n  @if (previewConfig && previewConfig.displayMode !== 'dashboard') {\n    <c8y-local-controls\n      [controls]=\"controls\"\n      [displayMode]=\"previewConfig.displayMode!\"\n      [config]=\"previewConfig\"\n      [disabled]=\"true\"\n    ></c8y-local-controls>\n  }\n\n  @if (previewConfig) {\n    <c8y-datapoints-list\n      [config]=\"previewConfig\"\n      [isInPreviewMode]=\"true\"\n      data-cy=\"c8y-datapoints-list-widget-config--preview-datapoints-list\"\n    ></c8y-datapoints-list>\n  }\n</ng-template>\n" }]
        }], propDecorators: { config: [{ type: i0.Input, args: [{ isSignal: true, alias: "config", required: true }] }], previewTemplate: [{ type: i0.ViewChild, args: ['dataPointsListPreview', { isSignal: true }] }] } });

/**
 * Generated bundle index. Do not edit.
 */

export { DatapointsListConfigComponent, DatapointsListFetchService, DatapointsListService, DatapointsListViewComponent };
//# sourceMappingURL=c8y-ngx-components-widgets-implementations-datapoints-list.mjs.map
