import * as i0 from '@angular/core';
import { Injectable, Pipe, Input, Directive, EventEmitter, Output, ChangeDetectionStrategy, Component, signal, Optional, inject, DestroyRef, ViewChild } from '@angular/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import * as i2$1 from '@angular/forms';
import { FormBuilder, NgForm, Validators, ReactiveFormsModule } from '@angular/forms';
import * as i1$2 from '@c8y/ngx-components';
import { DynamicComponentAlertAggregator, DynamicComponentAlert, CommonModule, DocsModule, DynamicComponentModule, ListGroupModule, VirtualScrollListenerDirective, ApplyRangeClassPipe, DismissAlertStrategy, CoreModule } from '@c8y/ngx-components';
import * as i4 from '@c8y/ngx-components/context-dashboard';
import { WidgetConfigComponent, WidgetConfigService } from '@c8y/ngx-components/context-dashboard';
import { DatapointSelectorModule } from '@c8y/ngx-components/datapoint-selector';
import * as i5 from '@c8y/ngx-components/global-context';
import { GLOBAL_CONTEXT_DISPLAY_MODE, PRESET_NAME, REFRESH_OPTION, GlobalContextConnectorComponent, LocalControlsComponent, AGGREGATION_LABELS, AGGREGATION_VALUES_ARR } from '@c8y/ngx-components/global-context';
import { INTERVAL_VALUES } from '@c8y/ngx-components/interval-picker';
import { PopoverModule } from 'ngx-bootstrap/popover';
import { auditTime, debounceTime as debounceTime$1 } from 'rxjs/operators';
import * as i2 from '@angular/common';
import { AsyncPipe } from '@angular/common';
import { gettext } from '@c8y/ngx-components/gettext';
import * as i1 from '@c8y/ngx-components/datapoints-export-selector';
import { DatapointsExportSelectorComponent } from '@c8y/ngx-components/datapoints-export-selector';
import * as i1$1 from '@ngx-translate/core';
import { merge } from 'lodash-es';
import { BehaviorSubject, Subject, debounceTime } from 'rxjs';
import { aggregationType } from '@c8y/client';

const DEFAULT_DPT_REFRESH_INTERVAL_VALUE = 30_000;
/**
 * Represents the legacy's global time context date selection of the widget.
 */
const DATE_SELECTION_VALUES = {
    dashboard_context: 'dashboard_context',
    config: 'config',
    view_and_config: 'view_and_config'
};
const DATE_SELECTION_VALUES_ARR = [
    DATE_SELECTION_VALUES.dashboard_context,
    DATE_SELECTION_VALUES.config,
    DATE_SELECTION_VALUES.view_and_config
];
const DATE_SELECTION_LABELS = {
    config: gettext('Widget configuration'),
    view_and_config: gettext('Widget and widget configuration'),
    dashboard_context: gettext('Dashboard time range')
};
const REFRESH_INTERVAL_VALUES_ARR = [5_000, 10_000, 15_000, 30_000, 60_000];
const RENDER_TYPES_LABELS = {
    min: gettext('Minimum'),
    max: gettext('Maximum'),
    area: gettext('Area')
};
const INTERVAL_VALUES_ARR = [
    INTERVAL_VALUES.minutes,
    INTERVAL_VALUES.hours,
    INTERVAL_VALUES.days,
    INTERVAL_VALUES.weeks,
    INTERVAL_VALUES.months,
    INTERVAL_VALUES.custom
];
const TIME_RANGE_INTERVAL_LABELS = {
    minutes: gettext('Last minute'),
    hours: gettext('Last hour'),
    days: gettext('Last day'),
    weeks: gettext('Last week'),
    months: gettext('Last month'),
    custom: gettext('Custom')
};
const DURATION_OPTIONS = [
    {
        id: INTERVAL_VALUES.minutes,
        label: TIME_RANGE_INTERVAL_LABELS.minutes,
        unit: INTERVAL_VALUES.minutes,
        amount: 1
    },
    {
        id: INTERVAL_VALUES.hours,
        label: TIME_RANGE_INTERVAL_LABELS.hours,
        unit: INTERVAL_VALUES.hours,
        amount: 1
    },
    {
        id: INTERVAL_VALUES.days,
        label: TIME_RANGE_INTERVAL_LABELS.days,
        unit: INTERVAL_VALUES.days,
        amount: 1
    },
    {
        id: INTERVAL_VALUES.weeks,
        label: TIME_RANGE_INTERVAL_LABELS.weeks,
        unit: INTERVAL_VALUES.weeks,
        amount: 1
    },
    {
        id: INTERVAL_VALUES.months,
        label: TIME_RANGE_INTERVAL_LABELS.months,
        unit: INTERVAL_VALUES.months,
        amount: 1
    },
    { id: INTERVAL_VALUES.custom, label: TIME_RANGE_INTERVAL_LABELS.custom }
];

function mapToSourceValueObject([key, value]) {
    return { key, value };
}
class DatapointsTableViewService {
    constructor(dataFetchingService) {
        this.dataFetchingService = dataFetchingService;
    }
    /**
     * Filters out inactive data points from the given array.
     *
     * @param datapoints - The array of data points to filter.
     * @returns An array of data points that are active.
     */
    filterOutInactiveDatapoints(datapoints) {
        return datapoints.filter((datapoint) => datapoint.__active);
    }
    hasMultipleDatapoints(datapoints) {
        const ids = datapoints.map(dp => dp.__target.id);
        return ids.length > 1;
    }
    /**
     * Returns a map of active data points device IDs with their corresponding series.
     *
     * Example output:
     * ```typescript
     * new Map([
     *   [
     *     "844657202",
     *     [
     *       "c8y_Temperature.T"
     *     ]
     *   ],
     *   [
     *     "32666427",
     *     [
     *       "c8y_Battery.Battery"
     *     ]
     *   ]
     * ]);
     * ```
     * @param datapoints - An array of data points.
     * @returns A map where the key is the data point ID and the value is an array of data point series.
     */
    groupSeriesByDeviceId(activeDatapoints) {
        return activeDatapoints.reduce((map, { fragment, series, __target: { id } }) => {
            const value = `${fragment}.${series}`;
            const existingValue = map.get(id) ?? [];
            map.set(id, [...existingValue, value]);
            return map;
        }, new Map());
    }
    /**
     * Retrieves the active data points series data and returns it as a map.
     *
     * @param datapointsValuesDataMap - A map of data point sources with their associated series.
     * @param config - The configuration of the data points table.
     * @param roundSeconds - Whether to round the seconds or not.
     *                       If true, the seconds will be rounded to 0.
     *                       If false, the seconds will be displayed as they are.
     * @returns A Promise that resolves to a Map object with data point IDs as keys and DataObject as values or undefined when all series has forbidden access.
     */
    async getAllActiveSeriesDataMap(datapointsValuesDataMap, config, roundSeconds) {
        const promises = Array.from(datapointsValuesDataMap).map(async ([source, series]) => {
            const params = {
                dateFrom: config.dateTimeContext.dateFrom,
                dateTo: config.dateTimeContext.dateTo,
                source,
                series,
                aggregationType: config.aggregation
            };
            const { data, res } = await this.dataFetchingService.fetchSeriesData(params, roundSeconds);
            return { source, data, res };
        });
        const results = await Promise.all(promises);
        const allSeriesHasForbiddenAccess = results.every(item => item.res?.status === 403);
        if (allSeriesHasForbiddenAccess) {
            throw new Error('Access forbidden: All series have a 403 status code');
        }
        const filteredResults = this.filterOutElementsWithForbiddenResponses(results);
        const resultMap = new Map();
        filteredResults.forEach(result => resultMap.set(result.source, result.data));
        return resultMap;
    }
    /**
     * Creates an array of DatapointsWithValues based on the provided datapoints and datapointsSeriesDataMap.
     *
     * Finds an index of a current data point within series object and based on that index filters values array.
     *
     * @param datapoints - An array of data points.
     * @param datapointsSeriesDataMap - A map containing series data for data points.
     * @returns An array of DatapointsWithValues.
     */
    getDatapointsWithValues(datapoints, datapointsSeriesDataMap) {
        return datapoints.map((dp) => {
            const seriesData = datapointsSeriesDataMap.get(dp.__target.id);
            if (!seriesData) {
                return { ...dp, values: {} };
            }
            // Find an index of a corresponding datapoint data, within series object.
            const datapointSeriesArrayIndex = seriesData.series.findIndex(s => s.name === dp.series && s.type === dp.fragment);
            const valuesFilteredByDatapointSeriesArrayIndex = Object.fromEntries(Object.entries(seriesData.values).map(([key, arr]) => [
                key,
                arr.filter((_, index) => index === datapointSeriesArrayIndex)
            ]));
            return {
                ...dp,
                seriesUnit: seriesData.series[datapointSeriesArrayIndex]?.unit,
                values: valuesFilteredByDatapointSeriesArrayIndex
            };
        });
    }
    /**
     * Creates the column headers for the devices in the data points table.
     *
     * @param datapointsWithValues - An array of data points.
     * @returns An array of column headers for the devices.
     */
    getColumnHeaders(datapointsWithValues) {
        return datapointsWithValues.map(({ __target: { name }, label, renderType, unit }) => {
            return { deviceName: name, label, renderType, unit };
        });
    }
    mapDatapointsWithValuesToList(datapointsWithValues) {
        return datapointsWithValues.flatMap(dp => {
            if (!dp.values) {
                return [];
            }
            return Object.entries(dp.values).flatMap(([date, valuesArray]) => {
                const value = this.findMinMaxValues(valuesArray);
                if (value == null) {
                    return [];
                }
                return [
                    {
                        dateAndTime: date,
                        deviceName: dp.__target.name,
                        fragment: dp.fragment,
                        label: dp.label,
                        redRangeMax: dp.redRangeMax,
                        redRangeMin: dp.redRangeMin,
                        renderType: dp.renderType,
                        series: dp.series,
                        value: value,
                        yellowRangeMax: dp.yellowRangeMax,
                        yellowRangeMin: dp.yellowRangeMin
                    }
                ];
            });
        });
    }
    /**
     * Finds the overall minimum and maximum values from an array of objects containing 'min' and 'max' properties.
     *
     * If the array contains only one object, that object's 'min' and 'max' values will be returned.
     *
     * @param valuesArray - An array with objects, where each contains 'min' and 'max' properties.
     * @returns An object with the smallest 'min' and largest 'max' values found in the array.
     *
     * @example
     * const values = [
     *   { min: 1, max: 10 }
     * ];
     *
     * const result = findMinMaxValues(values);
     * // result is { min: 1, max: 10 }
     */
    findMinMaxValues(valuesArray) {
        const validItems = valuesArray.filter((item) => item && item.min != null && item.max != null);
        if (validItems.length === 0) {
            return null;
        }
        const initialValue = { min: validItems[0].min, max: validItems[0].max };
        return validItems.reduce((acc, item) => ({
            min: Math.min(acc.min, item.min),
            max: Math.max(acc.max, item.max)
        }), initialValue);
    }
    /**
     * Groups a list of data points by date and device, based on given references.
     *
     * @param dataList - The list of data points to be grouped.
     * @param references - The column headers that serve as references for grouping.
     * @returns An array of grouped data points, where each group corresponds to a unique date and device.
     */
    groupByDateAndDevice(dataList, references) {
        const map = this.generateDataPointMap(dataList, references);
        return this.mergeDatapoints(map);
    }
    /**
     * Generates and populates a map with data points.
     *
     * This function processes the provided data points and organizes them into a map structure
     * where each key is a unique combination of date and device identifiers, and the value is an
     * array of data points (or null values) associated with that key. This structured data is then
     * used in the data point table to render the data appropriately.
     *
     * @param dataList - The list of data point table items to be processed.
     * @param columnsHeadersReferences - The list of column headers used to determine the order and structure of the map values.
     * @returns A map where the key is a datapoint identifier containing the date and device name, and the value is an array of data point table items or null.
     */
    generateDataPointMap(dataList, columnsHeadersReferences) {
        // Map to store the data points indexed by a unique identifier.
        const map = new Map();
        dataList.forEach(obj => {
            // Generate a unique identifier for each data point using the date and device name.
            const dateKey = this.toISOFormat(obj.dateAndTime);
            const datapointIdentifier = `${dateKey}_${obj.deviceName}`;
            // Initialize the map entry if it does not exist with a unique identifier.
            if (!map.has(datapointIdentifier)) {
                map.set(datapointIdentifier, Array(columnsHeadersReferences.length).fill(null));
            }
            // Find the index of the reference that matches the current data point.
            const matchingColumnIndex = columnsHeadersReferences.findIndex(ref => ref.deviceName === obj.deviceName && ref.label === obj.label);
            // Update the map entry with the data point.
            const tableItem = map.get(datapointIdentifier);
            if (tableItem) {
                tableItem[matchingColumnIndex] = { ...obj };
            }
        });
        return map;
    }
    /**
     * Merges the data points from the given map into an array of grouped data point table items.
     *
     * @param map - The map containing the data points to be merged.
     * @returns An array of grouped data point table items.
     */
    mergeDatapoints(map) {
        const mergedData = [];
        map.forEach((values, datapointIdentifier) => {
            const [dateKey, deviceName] = datapointIdentifier.split('_');
            const validDataPoint = values.some(item => item && Object.keys(item.value).length > 0);
            if (validDataPoint) {
                mergedData.push({
                    dateAndTime: dateKey,
                    deviceName: deviceName,
                    rowItems: values.map(item => (item ? { ...item } : null))
                });
            }
        });
        return mergedData;
    }
    sortDataByDateDescending(data) {
        return data.sort((a, b) => new Date(b.dateAndTime).getTime() - new Date(a.dateAndTime).getTime());
    }
    /**
     * Prepares the updated time range based on the selected interval.
     *
     * In case of a 'custom' interval or no quantity, the original date range is returned.
     *
     * @param interval - The selected interval type.
     * @returns An object containing the `dateFrom` and `dateTo` in ISO string format.
     */
    prepareTimeRange(interval, dateFromInput, dateToInput) {
        if (!interval || interval === INTERVAL_VALUES.custom) {
            return { dateFrom: dateFromInput, dateTo: dateToInput };
        }
        const selected = DURATION_OPTIONS.find(selectedDuration => selectedDuration.id === interval);
        if (!selected?.amount) {
            return { dateFrom: dateFromInput, dateTo: dateToInput };
        }
        const now = new Date();
        return {
            dateFrom: this.subtractTime(now, selected.amount, selected.unit).toISOString(),
            dateTo: now.toISOString()
        };
    }
    /**
     * Subtracts an amount of time from a given date.
     *
     * @param date - The original date.
     * @param amount - The amount of time units to subtract.
     * @param unit - The unit of time to subtract (e.g., minutes, hours, days, weeks, months).
     * @returns A new date with the specified time subtracted.
     */
    subtractTime(date, amount, unit) {
        const newDate = new Date(date);
        switch (unit) {
            case INTERVAL_VALUES.minutes:
                newDate.setUTCMinutes(newDate.getUTCMinutes() - amount);
                break;
            case INTERVAL_VALUES.hours:
                newDate.setUTCHours(newDate.getUTCHours() - amount);
                break;
            case INTERVAL_VALUES.days:
                newDate.setUTCDate(newDate.getUTCDate() - amount);
                break;
            case INTERVAL_VALUES.weeks:
                newDate.setUTCDate(newDate.getUTCDate() - amount * 7);
                break;
            case INTERVAL_VALUES.months:
                this.subtractMonthsAndAdjustDay(newDate, amount);
                break;
        }
        return newDate;
    }
    getSeriesWithoutPermissionToRead(activeDatapointsSeriesData, activeDatapointsIdsWithSeries) {
        if (!activeDatapointsSeriesData) {
            // Returns all activeDatapointsIdsWithSeries entries if activeDatapointsSeriesData is undefined.
            // It means that the user does not have permission to see any of the selected datapoints data.
            return Array.from(activeDatapointsIdsWithSeries, mapToSourceValueObject);
        }
        const availableSources = new Set(activeDatapointsSeriesData.keys());
        return Array.from(activeDatapointsIdsWithSeries)
            .filter(([source]) => !availableSources.has(source))
            .map(mapToSourceValueObject);
    }
    hasSecondsAndMillisecondsEqualZero(timeString) {
        if (!timeString) {
            return false;
        }
        const date = new Date(timeString);
        if (isNaN(date.getTime())) {
            return false;
        }
        return date.getUTCSeconds() === 0 && date.getUTCMilliseconds() === 0;
    }
    /**
     * Converts a date string to ISO format.
     *
     * @param dateStr - The date string to convert.
     * @returns The ISO format of the given date string.
     */
    toISOFormat(dateStr) {
        return new Date(dateStr).toISOString();
    }
    filterOutElementsWithForbiddenResponses(seriesDataWithResponse) {
        return seriesDataWithResponse.filter(item => {
            return !(item.res?.status === 403);
        });
    }
    subtractMonthsAndAdjustDay(date, monthsToSubtract) {
        const currentMonth = date.getUTCMonth();
        const expectedTargetMonth = this.calculateTargetMonth(currentMonth, monthsToSubtract);
        date.setUTCMonth(currentMonth - monthsToSubtract);
        const actualMonth = date.getUTCMonth();
        const dayDoesNotExistInTargetMonth = actualMonth !== expectedTargetMonth;
        if (dayDoesNotExistInTargetMonth) {
            this.setToLastDayOfPreviousMonth(date);
        }
    }
    /**
     * Calculates the target month number (0-11) after subtracting months from the current month.
     * Handles negative month numbers by normalizing them to the valid 0-11 range.
     *
     * Examples:
     * - January(0) - 1 month = December(11)
     * - March(2) - 4 months = November(10)
     * - December(11) - 1 month = November(10)
     *
     * @param currentMonth - Current month (0-11, where 0 is January)
     * @param monthsToSubtract - Number of months to subtract
     * @returns Normalized month number in range 0-11
     */
    calculateTargetMonth(currentMonth, monthsToSubtract) {
        return (currentMonth - monthsToSubtract + 12) % 12;
    }
    /**
     * Sets the date to the last day of the previous month.
     * Using 0 as dateValue makes JavaScript automatically calculate
     * last day of previous month, per JavaScript Date API behavior.
     * @param date - Date to modify
     */
    setToLastDayOfPreviousMonth(date) {
        date.setUTCDate(0);
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: DatapointsTableViewService, deps: [{ token: i1.DataFetchingService }], target: i0.ɵɵFactoryTarget.Injectable }); }
    static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: DatapointsTableViewService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: DatapointsTableViewService, decorators: [{
            type: Injectable,
            args: [{
                    providedIn: 'root'
                }]
        }], ctorParameters: () => [{ type: i1.DataFetchingService }] });

/**
 * A pipe that adjusts the aggregated time range based on the aggregation type.
 *
 * ```html
 * '9:00' | adjustAggregatedTimeRange: config.aggregation (e.g.:HOURLY)
 * ```
 * The output will be '9:00-10:00'.
 */
class AdjustAggregatedTimeRangePipe {
    /**
     * Transforms the input time based on the aggregation type.
     * @param inputTime The input time string.
     * @param aggregationType The type of aggregation (optional).
     * @returns The transformed time string.
     */
    transform(inputTime, aggregationType$1) {
        if (!aggregationType$1) {
            return inputTime;
        }
        if (aggregationType$1 === aggregationType.DAILY) {
            return '';
        }
        const date = this.createDateFromInput(inputTime);
        const isTwelveHoursFormat = this.isTwelveHoursFormat(inputTime);
        switch (aggregationType$1) {
            case aggregationType.HOURLY:
                return this.getHourlyTimeRange(date, isTwelveHoursFormat);
            case aggregationType.MINUTELY:
                return this.getMinutelyTimeRange(date, isTwelveHoursFormat);
            default:
                throw new Error('Unsupported aggregation type');
        }
    }
    /**
     * Creates a date object from the input time string.
     * @param inputTime The input time string.
     * @returns The created Date object.
     */
    createDateFromInput(inputTime) {
        const defaultDate = '1970-01-01 ';
        const isPM = /PM/i.test(inputTime);
        const cleanedTime = inputTime.replace(/AM|PM/i, '').trim();
        this.validateTimeFormat(cleanedTime, inputTime);
        const dateTimeString = `${defaultDate}${cleanedTime}`;
        const date = new Date(dateTimeString);
        if (isNaN(date.getTime())) {
            throw new Error('Invalid input time');
        }
        return this.adjustForPMTime(date, isPM);
    }
    /**
     * Validates if the time string matches the required format and has valid values.
     * @param time The time string to validate.
     * @param originalInput The original input string (including AM/PM if present).
     * @throws Error if the time format is invalid or values are out of range.
     */
    validateTimeFormat(time, originalInput) {
        const parts = time.split(':');
        this.validateTimeParts(parts);
        const [hoursStr, minutesStr, secondsStr] = parts;
        this.validateTimeDigits(hoursStr, minutesStr, secondsStr);
        const { hours, minutes, seconds } = this.parseTimeComponents(hoursStr, minutesStr, secondsStr);
        this.validateTimeRanges(hours, minutes, seconds);
        this.validateTimeFormat24Hour(hours, originalInput);
    }
    validateTimeParts(parts) {
        if (parts.length < 2 || parts.length > 3) {
            throw new Error('Invalid input time');
        }
    }
    validateTimeDigits(hoursStr, minutesStr, secondsStr) {
        if (!this.isValidNumberString(hoursStr) ||
            !this.isValidNumberString(minutesStr) ||
            (secondsStr !== undefined && !this.isValidNumberString(secondsStr))) {
            throw new Error('Invalid input time');
        }
    }
    parseTimeComponents(hoursStr, minutesStr, secondsStr) {
        return {
            hours: Number(hoursStr),
            minutes: Number(minutesStr),
            seconds: secondsStr ? Number(secondsStr) : 0
        };
    }
    validateTimeRanges(hours, minutes, seconds) {
        if (hours > 23 || hours < 0 || minutes > 59 || minutes < 0 || seconds > 59 || seconds < 0) {
            throw new Error('Invalid input time');
        }
    }
    validateTimeFormat24Hour(hours, originalInput) {
        if (hours > 12 && this.hasAmPm(originalInput)) {
            throw new Error('Invalid input time');
        }
    }
    /**
     * Checks if string contains only digits and is 1-2 characters long.
     * @param value String to check
     * @returns boolean indicating if string is valid
     */
    isValidNumberString(value) {
        return (value.length > 0 &&
            value.length <= 2 &&
            value.split('').every(char => char >= '0' && char <= '9'));
    }
    /**
     * Checks if the input time has AM/PM markers.
     * @param input The input time string to check.
     * @returns boolean indicating if the input contains AM/PM.
     */
    hasAmPm(input) {
        return /AM|PM/i.test(input);
    }
    /**
     * Adjusts the date for PM times by adding 12 hours when necessary.
     * @param date The date object to adjust.
     * @param isPM Boolean indicating if the time is PM.
     * @returns The adjusted Date object.
     */
    adjustForPMTime(date, isPM) {
        const hours = date.getHours();
        if (isPM && hours < 12) {
            date.setHours(hours + 12);
        }
        else if (!isPM && hours === 12) {
            date.setHours(0);
        }
        return date;
    }
    /**
     * Checks if the input time is in twelve hours format.
     * @param inputTime The input time string.
     * @returns True if the input time is in twelve hours format, false otherwise.
     */
    isTwelveHoursFormat(inputTime) {
        return /AM|PM/i.test(inputTime);
    }
    /**
     * Gets the hourly time range for the given date.
     * @param date The date object.
     * @param twelveHoursFormat Indicates whether to use twelve hours format.
     * @returns The hourly time range string.
     */
    getHourlyTimeRange(date, twelveHoursFormat) {
        const nextHour = new Date(date.getTime());
        nextHour.setHours(date.getHours() + 1);
        return `${this.formatTime(date, twelveHoursFormat, true)}-${this.formatTime(nextHour, twelveHoursFormat, true)}`;
    }
    /**
     * Gets the minutely time range for the given date.
     * @param date The date object.
     * @param twelveHoursFormat Indicates whether to use twelve hours format.
     * @returns The minutely time range string.
     */
    getMinutelyTimeRange(date, twelveHoursFormat) {
        const nextMinute = new Date(date.getTime());
        nextMinute.setMinutes(date.getMinutes() + 1);
        return `${this.formatTime(date, twelveHoursFormat, false)}-${this.formatTime(nextMinute, twelveHoursFormat, false)}`;
    }
    /**
     * Formats the given date into a time string.
     * @param date The date to format.
     * @param usePeriod Indicates whether to include the period (AM/PM) in the formatted time.
     * @param useHourOnly Indicates whether to include only the hour part in the formatted time.
     * @returns The formatted time string.
     */
    formatTime(date, usePeriod, useHourOnly) {
        const hours = date.getHours();
        const minutes = date.getMinutes().toString().padStart(2, '0');
        if (usePeriod) {
            const period = hours >= 12 ? 'PM' : 'AM';
            const formattedHours = hours % 12 === 0 ? 12 : hours % 12;
            return `${formattedHours}:${useHourOnly ? '00' : minutes} ${period}`;
        }
        else {
            return `${hours.toString().padStart(2, '0')}:${useHourOnly ? '00' : minutes}`;
        }
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: AdjustAggregatedTimeRangePipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
    static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "20.3.19", ngImport: i0, type: AdjustAggregatedTimeRangePipe, isStandalone: true, name: "adjustAggregatedTimeRange" }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: AdjustAggregatedTimeRangePipe, decorators: [{
            type: Pipe,
            args: [{
                    name: 'adjustAggregatedTimeRange',
                    standalone: true
                }]
        }] });

/**
 * Creates a column header title message.
 *
 * ```html
 * title="{{ header | columnTitle }}"
 * ```
 * The output will be e.g.: 'c8y_Temperature → T [T] (Area)'.
 */
class ColumnTitlePipe {
    constructor(translateService) {
        this.translateService = translateService;
    }
    /**
     * Transforms the column header into a formatted string with label and optionally unit and render type.
     *
     * @param columnHeader - The column header object.
     * @returns The formatted string with label, unit, and render type.
     */
    transform(columnHeader) {
        const label = columnHeader.label.trim();
        const unit = columnHeader.unit ? `[${columnHeader.unit.trim()}]` : '';
        const renderType = columnHeader.renderType
            ? this.translateService.instant(RENDER_TYPES_LABELS[columnHeader.renderType])
            : '';
        if (!renderType) {
            return `${label} ${unit}`.trim();
        }
        return `${label} ${unit} (${renderType})`.trim();
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: ColumnTitlePipe, deps: [{ token: i1$1.TranslateService }], target: i0.ɵɵFactoryTarget.Pipe }); }
    static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "20.3.19", ngImport: i0, type: ColumnTitlePipe, isStandalone: true, name: "columnTitle" }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: ColumnTitlePipe, decorators: [{
            type: Pipe,
            args: [{
                    name: 'columnTitle',
                    standalone: true
                }]
        }], ctorParameters: () => [{ type: i1$1.TranslateService }] });

class DynamicColumnDirective {
    constructor(el, renderer) {
        this.el = el;
        this.renderer = renderer;
    }
    ngOnInit() {
        this.updateColumnClass();
    }
    updateColumnClass() {
        let className = '';
        switch (this.numberOfColumns) {
            case 1:
                className = 'col-md-12';
                break;
            case 2:
                className = 'col-md-6';
                break;
            default:
                if (this.numberOfColumns >= 3) {
                    className = 'col-md-3';
                }
                break;
        }
        if (className) {
            this.renderer.addClass(this.el.nativeElement, className);
        }
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: DynamicColumnDirective, deps: [{ token: i0.ElementRef }, { token: i0.Renderer2 }], target: i0.ɵɵFactoryTarget.Directive }); }
    static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.19", type: DynamicColumnDirective, isStandalone: true, selector: "[c8yDynamicColumn]", inputs: { numberOfColumns: ["c8yDynamicColumn", "numberOfColumns"] }, ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: DynamicColumnDirective, decorators: [{
            type: Directive,
            args: [{
                    selector: '[c8yDynamicColumn]',
                    standalone: true
                }]
        }], ctorParameters: () => [{ type: i0.ElementRef }, { type: i0.Renderer2 }], propDecorators: { numberOfColumns: [{
                type: Input,
                args: ['c8yDynamicColumn']
            }] } });

class DatapointsTableComponent {
    constructor() {
        this.isScrolling = new EventEmitter();
        this.hasNoPermissionsToReadAnyMeasurement = false;
        this.missingAllPermissionsAlert = new DynamicComponentAlertAggregator();
        /**
         * Default fraction size format for numbers with decimal places.
         */
        this.fractionSize = '1.2-2';
    }
    ngOnChanges() {
        if (typeof this.decimalPlaces === 'number' && !Number.isNaN(this.decimalPlaces)) {
            this.fractionSize = `1.${this.decimalPlaces}-${this.decimalPlaces}`;
        }
        if (!this.seriesWithoutPermissionToReadCount) {
            return;
        }
        this.missingAllPermissionsAlert.clear();
        this.handleNoPermissionErrorMessage();
    }
    onListScrolled() {
        this.isScrolling.emit(true);
    }
    onListScrolledToTop() {
        this.isScrolling.emit(false);
    }
    getRangeValues(row) {
        return {
            yellowRangeMin: row.yellowRangeMin,
            yellowRangeMax: row.yellowRangeMax,
            redRangeMin: row.redRangeMin,
            redRangeMax: row.redRangeMax
        };
    }
    /**
     * Determines the fraction size format based on whether the number is an integer or has decimal places.
     *
     * @param value - The number to be formatted.
     * @returns Returns '1.0-0' if the number is an integer, otherwise returns the current fraction size.
     */
    getFractionSize(value) {
        return value % 1 === 0 ? '1.0-0' : this.fractionSize;
    }
    handleNoPermissionErrorMessage() {
        this.hasNoPermissionsToReadAnyMeasurement =
            this.seriesWithoutPermissionToReadCount === this.devicesColumnHeaders.length;
        if (this.hasNoPermissionsToReadAnyMeasurement) {
            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: DatapointsTableComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.19", type: DatapointsTableComponent, isStandalone: true, selector: "c8y-datapoints-table", inputs: { aggregationType: "aggregationType", datapointsTableItems: "datapointsTableItems", devicesColumnHeaders: "devicesColumnHeaders", decimalPlaces: "decimalPlaces", hasMultipleDatapoints: "hasMultipleDatapoints", isLoading: "isLoading", seriesWithoutPermissionToReadCount: "seriesWithoutPermissionToReadCount" }, outputs: { isScrolling: "isScrolling" }, host: { classAttribute: "d-col flex-grow" }, usesOnChanges: true, ngImport: i0, template: "@if (!hasNoPermissionsToReadAnyMeasurement) {\n  <div class=\"c8y-cq-440\">\n    <div\n      class=\"hidden-xs c8y-list__item c8y-list--timeline hidden-cq\"\n      [ngClass]=\"{ 'separator-top-bottom': devicesColumnHeaders.length > 0 }\"\n    >\n      <div class=\"d-flex container-fluid\">\n        <div class=\"c8y-list--timeline__item__date\"></div>\n        <div class=\"c8y-list__item__block flex-grow min-width-0\">\n          <div class=\"c8y-list__item__body\">\n            <div class=\"d-flex row\">\n              @if (hasMultipleDatapoints) {\n                <div\n                  class=\"min-width-0\"\n                  [title]=\"'Device' | translate\"\n                  [c8yDynamicColumn]=\"devicesColumnHeaders.length\"\n                >\n                  <span class=\"text-medium text-truncate\">\n                    {{ 'Device' | translate }}\n                  </span>\n                </div>\n              }\n              <!-- Data points column headers -->\n              @for (header of devicesColumnHeaders; track header) {\n                <div\n                  class=\"min-width-0\"\n                  title=\"{{ header | columnTitle }}\"\n                  [c8yDynamicColumn]=\"devicesColumnHeaders.length\"\n                >\n                  <span class=\"text-medium text-truncate\">\n                    {{ header.label }} {{ header.unit }}\n                  </span>\n                </div>\n              }\n            </div>\n          </div>\n        </div>\n      </div>\n    </div>\n  </div>\n  <!-- The record list -->\n  @if (!isLoading) {\n    @if (datapointsTableItems.length) {\n      <c8y-list-group\n        class=\"p-t-8 flex-grow c8y-cq-440\"\n        c8yVirtualScrollListener\n        (scrolled)=\"onListScrolled()\"\n        (scrolledToTop)=\"onListScrolledToTop()\"\n      >\n        <c8y-li-timeline\n          *c8yFor=\"\n            let tableItem of { data: datapointsTableItems, res: null };\n            enableVirtualScroll: true;\n            virtualScrollElementSize: 48;\n            virtualScrollStrategy: 'fixed'\n          \"\n        >\n          {{ tableItem.dateAndTime | c8yDate: 'mediumDate' }}\n          {{\n            tableItem.dateAndTime\n              | c8yDate: 'mediumTime'\n              | adjustAggregatedTimeRange: aggregationType\n          }}\n          <c8y-li>\n            <c8y-li-body>\n              <div class=\"d-flex row\">\n                @if (devicesColumnHeaders.length > 1) {\n                  <div\n                    class=\"min-width-0\"\n                    [c8yDynamicColumn]=\"devicesColumnHeaders.length\"\n                    [attr.data-label]=\"'Device' | translate\"\n                  >\n                    <div\n                      class=\"text-truncate\"\n                      title=\"{{ tableItem.deviceName }}\"\n                    >\n                      {{ tableItem.deviceName }}\n                    </div>\n                  </div>\n                }\n                <!-- Data point value row cells -->\n\n                @for (row of tableItem.rowItems; track row) {\n                  @if (row !== null) {\n                    @switch (row.renderType) {\n                      @case ('min') {\n                        <div\n                          [c8yDynamicColumn]=\"devicesColumnHeaders.length\"\n                          [ngClass]=\"row.value.min ?? null | applyRangeClass: getRangeValues(row)\"\n                          [attr.data-label]=\"row.label\"\n                          data-cy=\"c8y-datapoints-table--value-min\"\n                        >\n                          <div\n                            class=\"text-truncate\"\n                            title=\"{{\n                              row.value.min ?? '' | number: getFractionSize(row.value.min)\n                            }}\"\n                          >\n                            {{ row.value.min ?? '' | number: getFractionSize(row.value.min) }}\n                          </div>\n                        </div>\n                      }\n                      @case ('max') {\n                        <div\n                          class=\"col-md-4\"\n                          [ngClass]=\"row.value.max ?? null | applyRangeClass: getRangeValues(row)\"\n                          data-cy=\"c8y-datapoints-table--value-max\"\n                        >\n                          <div\n                            class=\"text-truncate\"\n                            title=\"{{\n                              row.value.max ?? '' | number: getFractionSize(row.value.max)\n                            }}\"\n                          >\n                            {{ row.value.max ?? '' | number: getFractionSize(row.value.max) }}\n                          </div>\n                        </div>\n                      }\n                      @case ('area') {\n                        <div\n                          [c8yDynamicColumn]=\"devicesColumnHeaders.length\"\n                          data-cy=\"c8y-datapoints-table--value-minmax\"\n                        >\n                          <span\n                            class=\"text-truncate\"\n                            title=\"{{\n                              row.value.min ?? '' | number: getFractionSize(row.value.min)\n                            }}\"\n                            [ngClass]=\"row.value.min ?? null | applyRangeClass: getRangeValues(row)\"\n                            data-cy=\"c8y-datapoints-table--value-minmax-min\"\n                          >\n                            {{ row.value.min ?? '' | number: getFractionSize(row.value.min) }}\n                          </span>\n                          ...\n                          <span\n                            class=\"text-truncate\"\n                            title=\"{{\n                              row.value.max ?? '' | number: getFractionSize(row.value.max)\n                            }}\"\n                            [ngClass]=\"row.value.max ?? null | applyRangeClass: getRangeValues(row)\"\n                            data-cy=\"c8y-datapoints-table--value-minmax-max\"\n                          >\n                            {{ row.value.max ?? '' | number: getFractionSize(row.value.max) }}\n                          </span>\n                        </div>\n                      }\n                      @default {\n                        <div [c8yDynamicColumn]=\"devicesColumnHeaders.length\">\n                          <span\n                            class=\"text-truncate\"\n                            title=\"{{\n                              row.value.min ?? '' | number: getFractionSize(row.value.min)\n                            }}\"\n                            [ngClass]=\"row.value.min ?? null | applyRangeClass: getRangeValues(row)\"\n                          >\n                            {{ row.value.min ?? '' | number: getFractionSize(row.value.min) }}\n                          </span>\n                        </div>\n                      }\n                    }\n                  } @else {\n                    <div [c8yDynamicColumn]=\"devicesColumnHeaders.length\"></div>\n                  }\n                }\n              </div>\n            </c8y-li-body>\n          </c8y-li>\n        </c8y-li-timeline>\n      </c8y-list-group>\n    } @else {\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-table-list--empty-state\"\n        >\n          <p c8y-guide-docs>\n            <small\n              translate\n              ngNonBindable\n            >\n              Find out more in the\n              <a c8y-guide-href=\"/docs/cockpit/widgets-collection/#data-point-table\">\n                user documentation</a\n              >.\n            </small>\n          </p>\n        </c8y-ui-empty-state>\n      </div>\n    }\n  } @else {\n    <c8y-loading></c8y-loading>\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", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: i1$2.EmptyStateComponent, selector: "c8y-ui-empty-state", inputs: ["icon", "title", "subtitle", "horizontal"] }, { kind: "directive", type: i1$2.C8yTranslateDirective, selector: "[translate],[ngx-translate]" }, { kind: "directive", type: i2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1$2.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: i1$2.LoadingComponent, selector: "c8y-loading", inputs: ["layout", "progress", "message"] }, { kind: "ngmodule", type: DocsModule }, { kind: "directive", type: i1$2.GuideHrefDirective, selector: "[c8y-guide-href]", inputs: ["c8y-guide-href"] }, { kind: "component", type: i1$2.GuideDocsComponent, selector: "[c8y-guide-docs]" }, { kind: "directive", type: DynamicColumnDirective, selector: "[c8yDynamicColumn]", inputs: ["c8yDynamicColumn"] }, { kind: "ngmodule", type: DynamicComponentModule }, { kind: "component", type: i1$2.DynamicComponentAlertsComponent, selector: "c8y-dynamic-component-alerts", inputs: ["alerts"] }, { kind: "ngmodule", type: ListGroupModule }, { kind: "component", type: i1$2.ListGroupComponent, selector: "c8y-list-group" }, { kind: "component", type: i1$2.ListItemComponent, selector: "c8y-list-item, c8y-li", inputs: ["active", "highlighted", "emptyActions", "dense", "collapsed", "selectable"], outputs: ["collapsedChange"] }, { kind: "component", type: i1$2.ListItemBodyComponent, selector: "c8y-list-item-body, c8y-li-body", inputs: ["body"] }, { kind: "component", type: i1$2.ListItemTimelineComponent, selector: "c8y-list-item-timeline, c8y-li-timeline" }, { kind: "directive", type: VirtualScrollListenerDirective, selector: "[c8yVirtualScrollListener]", inputs: ["scrollThreshold"], outputs: ["scrolled", "scrolledToTop"] }, { kind: "pipe", type: AdjustAggregatedTimeRangePipe, name: "adjustAggregatedTimeRange" }, { kind: "pipe", type: ApplyRangeClassPipe, name: "applyRangeClass" }, { kind: "pipe", type: ColumnTitlePipe, name: "columnTitle" }, { kind: "pipe", type: i1$2.C8yTranslatePipe, name: "translate" }, { kind: "pipe", type: i2.DecimalPipe, name: "number" }, { kind: "pipe", type: i1$2.DatePipe, name: "c8yDate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: DatapointsTableComponent, decorators: [{
            type: Component,
            args: [{ selector: 'c8y-datapoints-table', host: { class: 'd-col flex-grow' }, standalone: true, imports: [
                        AdjustAggregatedTimeRangePipe,
                        ApplyRangeClassPipe,
                        ColumnTitlePipe,
                        CommonModule,
                        DocsModule,
                        DynamicColumnDirective,
                        DynamicComponentModule,
                        ListGroupModule,
                        VirtualScrollListenerDirective
                    ], changeDetection: ChangeDetectionStrategy.OnPush, template: "@if (!hasNoPermissionsToReadAnyMeasurement) {\n  <div class=\"c8y-cq-440\">\n    <div\n      class=\"hidden-xs c8y-list__item c8y-list--timeline hidden-cq\"\n      [ngClass]=\"{ 'separator-top-bottom': devicesColumnHeaders.length > 0 }\"\n    >\n      <div class=\"d-flex container-fluid\">\n        <div class=\"c8y-list--timeline__item__date\"></div>\n        <div class=\"c8y-list__item__block flex-grow min-width-0\">\n          <div class=\"c8y-list__item__body\">\n            <div class=\"d-flex row\">\n              @if (hasMultipleDatapoints) {\n                <div\n                  class=\"min-width-0\"\n                  [title]=\"'Device' | translate\"\n                  [c8yDynamicColumn]=\"devicesColumnHeaders.length\"\n                >\n                  <span class=\"text-medium text-truncate\">\n                    {{ 'Device' | translate }}\n                  </span>\n                </div>\n              }\n              <!-- Data points column headers -->\n              @for (header of devicesColumnHeaders; track header) {\n                <div\n                  class=\"min-width-0\"\n                  title=\"{{ header | columnTitle }}\"\n                  [c8yDynamicColumn]=\"devicesColumnHeaders.length\"\n                >\n                  <span class=\"text-medium text-truncate\">\n                    {{ header.label }} {{ header.unit }}\n                  </span>\n                </div>\n              }\n            </div>\n          </div>\n        </div>\n      </div>\n    </div>\n  </div>\n  <!-- The record list -->\n  @if (!isLoading) {\n    @if (datapointsTableItems.length) {\n      <c8y-list-group\n        class=\"p-t-8 flex-grow c8y-cq-440\"\n        c8yVirtualScrollListener\n        (scrolled)=\"onListScrolled()\"\n        (scrolledToTop)=\"onListScrolledToTop()\"\n      >\n        <c8y-li-timeline\n          *c8yFor=\"\n            let tableItem of { data: datapointsTableItems, res: null };\n            enableVirtualScroll: true;\n            virtualScrollElementSize: 48;\n            virtualScrollStrategy: 'fixed'\n          \"\n        >\n          {{ tableItem.dateAndTime | c8yDate: 'mediumDate' }}\n          {{\n            tableItem.dateAndTime\n              | c8yDate: 'mediumTime'\n              | adjustAggregatedTimeRange: aggregationType\n          }}\n          <c8y-li>\n            <c8y-li-body>\n              <div class=\"d-flex row\">\n                @if (devicesColumnHeaders.length > 1) {\n                  <div\n                    class=\"min-width-0\"\n                    [c8yDynamicColumn]=\"devicesColumnHeaders.length\"\n                    [attr.data-label]=\"'Device' | translate\"\n                  >\n                    <div\n                      class=\"text-truncate\"\n                      title=\"{{ tableItem.deviceName }}\"\n                    >\n                      {{ tableItem.deviceName }}\n                    </div>\n                  </div>\n                }\n                <!-- Data point value row cells -->\n\n                @for (row of tableItem.rowItems; track row) {\n                  @if (row !== null) {\n                    @switch (row.renderType) {\n                      @case ('min') {\n                        <div\n                          [c8yDynamicColumn]=\"devicesColumnHeaders.length\"\n                          [ngClass]=\"row.value.min ?? null | applyRangeClass: getRangeValues(row)\"\n                          [attr.data-label]=\"row.label\"\n                          data-cy=\"c8y-datapoints-table--value-min\"\n                        >\n                          <div\n                            class=\"text-truncate\"\n                            title=\"{{\n                              row.value.min ?? '' | number: getFractionSize(row.value.min)\n                            }}\"\n                          >\n                            {{ row.value.min ?? '' | number: getFractionSize(row.value.min) }}\n                          </div>\n                        </div>\n                      }\n                      @case ('max') {\n                        <div\n                          class=\"col-md-4\"\n                          [ngClass]=\"row.value.max ?? null | applyRangeClass: getRangeValues(row)\"\n                          data-cy=\"c8y-datapoints-table--value-max\"\n                        >\n                          <div\n                            class=\"text-truncate\"\n                            title=\"{{\n                              row.value.max ?? '' | number: getFractionSize(row.value.max)\n                            }}\"\n                          >\n                            {{ row.value.max ?? '' | number: getFractionSize(row.value.max) }}\n                          </div>\n                        </div>\n                      }\n                      @case ('area') {\n                        <div\n                          [c8yDynamicColumn]=\"devicesColumnHeaders.length\"\n                          data-cy=\"c8y-datapoints-table--value-minmax\"\n                        >\n                          <span\n                            class=\"text-truncate\"\n                            title=\"{{\n                              row.value.min ?? '' | number: getFractionSize(row.value.min)\n                            }}\"\n                            [ngClass]=\"row.value.min ?? null | applyRangeClass: getRangeValues(row)\"\n                            data-cy=\"c8y-datapoints-table--value-minmax-min\"\n                          >\n                            {{ row.value.min ?? '' | number: getFractionSize(row.value.min) }}\n                          </span>\n                          ...\n                          <span\n                            class=\"text-truncate\"\n                            title=\"{{\n                              row.value.max ?? '' | number: getFractionSize(row.value.max)\n                            }}\"\n                            [ngClass]=\"row.value.max ?? null | applyRangeClass: getRangeValues(row)\"\n                            data-cy=\"c8y-datapoints-table--value-minmax-max\"\n                          >\n                            {{ row.value.max ?? '' | number: getFractionSize(row.value.max) }}\n                          </span>\n                        </div>\n                      }\n                      @default {\n                        <div [c8yDynamicColumn]=\"devicesColumnHeaders.length\">\n                          <span\n                            class=\"text-truncate\"\n                            title=\"{{\n                              row.value.min ?? '' | number: getFractionSize(row.value.min)\n                            }}\"\n                            [ngClass]=\"row.value.min ?? null | applyRangeClass: getRangeValues(row)\"\n                          >\n                            {{ row.value.min ?? '' | number: getFractionSize(row.value.min) }}\n                          </span>\n                        </div>\n                      }\n                    }\n                  } @else {\n                    <div [c8yDynamicColumn]=\"devicesColumnHeaders.length\"></div>\n                  }\n                }\n              </div>\n            </c8y-li-body>\n          </c8y-li>\n        </c8y-li-timeline>\n      </c8y-list-group>\n    } @else {\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-table-list--empty-state\"\n        >\n          <p c8y-guide-docs>\n            <small\n              translate\n              ngNonBindable\n            >\n              Find out more in the\n              <a c8y-guide-href=\"/docs/cockpit/widgets-collection/#data-point-table\">\n                user documentation</a\n              >.\n            </small>\n          </p>\n        </c8y-ui-empty-state>\n      </div>\n    }\n  } @else {\n    <c8y-loading></c8y-loading>\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" }]
        }], propDecorators: { aggregationType: [{
                type: Input
            }], datapointsTableItems: [{
                type: Input
            }], devicesColumnHeaders: [{
                type: Input
            }], decimalPlaces: [{
                type: Input
            }], hasMultipleDatapoints: [{
                type: Input
            }], isLoading: [{
                type: Input
            }], seriesWithoutPermissionToReadCount: [{
                type: Input
            }], isScrolling: [{
                type: Output
            }] } });

class DatapointsTableViewWidgetComponent {
    constructor(alertService, dashboardChild, datapointsTableViewService, translateService, dashboardContextComponent, widgetConfigMigrationService) {
        this.alertService = alertService;
        this.dashboardChild = dashboardChild;
        this.datapointsTableViewService = datapointsTableViewService;
        this.translateService = translateService;
        this.dashboardContextComponent = dashboardContextComponent;
        this.widgetConfigMigrationService = widgetConfigMigrationService;
        /**
         * Indicates whether the component is in widget preview mode.
         * If true, the table will be displayed without the export selector button.
         */
        this.isInPreviewMode = false;
        /**
         * Represents the data points where __active property is set to true.
         */
        this.activeDatapoints = [];
        /**
         * An array of `GroupedDatapointTableItem` objects representing the datapoints table items.
         * Used to populate the table with data.
         */
        this.datapointsTableItems = [];
        /**
         * Indicates whether refreshing should be enabled or disabled.
         * It's 'true' when user is not allowed to view a measurements.
         */
        this.isRefreshDisabled = false;
        /**
         * Indicates whether the component is in the initial request state
         * where data for a table structure is being prepared.
         */
        this.isInitialRequest = true;
        /**
         * Current isLoading state. Indicates whether the data is being loaded.
         */
        this.isLoading$ = new BehaviorSubject(true);
        this.isScrolling = signal(false, ...(ngDevMode ? [{ debugName: "isScrolling" }] : []));
        this.displayMode = signal(GLOBAL_CONTEXT_DISPLAY_MODE.DASHBOARD, ...(ngDevMode ? [{ debugName: "displayMode" }] : []));
        this.contextConfig = signal({}, ...(ngDevMode ? [{ debugName: "contextConfig" }] : []));
        this.isLinkedToGlobal = signal(undefined, ...(ngDevMode ? [{ debugName: "isLinkedToGlobal" }] : []));
        this.widgetControls = signal(PRESET_NAME.DATA_TABLE, ...(ngDevMode ? [{ debugName: "widgetControls" }] : []));
        this.GLOBAL_CONTEXT_DISPLAY_MODE = GLOBAL_CONTEXT_DISPLAY_MODE;
        this.TIMEOUT_ERROR_TEXT = gettext('The request is taking longer than usual. We apologize for the inconvenience.');
        this.SERVER_ERROR_TEXT = gettext('Server error occurred.');
        this.destroy$ = new Subject();
        this.scrollingSubject$ = new Subject();
        /**
         * Indicates if the alert has already been displayed and can be dismissed.
         * The message is only displayed when a component is initialized.
         */
        this.isMissingAnyPermissionAlertShown = false;
    }
    async ngOnInit() {
        if (this.isInPreviewMode) {
            return;
        }
        this.setScrollingSubscription();
        const migratedConfig = this.widgetConfigMigrationService.migrateWidgetConfig(this.config);
        this.config = merge(this.config, migratedConfig);
        const displayMode = this.config.displayMode || GLOBAL_CONTEXT_DISPLAY_MODE.DASHBOARD;
        this.displayMode.set(displayMode);
        const initialContextConfig = {
            dateTimeContext: this.config.dateTimeContext,
            aggregation: this.config.aggregation,
            isAutoRefreshEnabled: this.config.isAutoRefreshEnabled,
            refreshInterval: this.config.refreshInterval,
            refreshOption: this.config.refreshOption
        };
        this.contextConfig.set(initialContextConfig);
        if (displayMode !== GLOBAL_CONTEXT_DISPLAY_MODE.DASHBOARD) {
            await this.prepareTableData(false);
            this.updateExportConfig();
        }
    }
    async ngOnChanges() {
        if (this.isInPreviewMode) {
            const hasAnyActiveDatapoint = this.checkIfHasAnyActiveDatapoint(this.config?.datapoints);
            if (!hasAnyActiveDatapoint) {
                return;
            }
            await this.prepareTableData(false);
        }
    }
    ngOnDestroy() {
        if (this.subscription) {
            this.subscription.unsubscribe();
        }
        this.destroy$.next();
        this.destroy$.complete();
    }
    onContextChange(event) {
        const { context, diff } = event;
        this.config = { ...this.config, ...context, dateTimeContext: { ...context.dateTimeContext } };
        // Allows widget to load on old version of dashboard
        if (context?.dateTimeContext?.interval !== undefined) {
            this.config = merge(this.config, { interval: context.dateTimeContext.interval });
        }
        this.contextConfig.set(context);
        // Prevent fetching data after disableAutoRefresh handler was triggered
        if (diff.isAutoRefreshEnabled === false &&
            Object.keys(diff).length === 1 &&
            context.refreshOption === REFRESH_OPTION.LIVE) {
            return;
        }
        void this.prepareTableData(false);
        this.updateExportConfig();
    }
    onRefresh() {
        void this.prepareTableData(false);
        this.updateExportConfig();
    }
    async onExportModalOpen(isOpened) {
        if (this.isInPreviewMode) {
            return;
        }
        const current = this.contextConfig();
        this.contextConfig.set({ ...current, isAutoRefreshEnabled: !isOpened });
        this.isLinkedToGlobal.set(!isOpened);
    }
    onScrolling(isScrolling) {
        if (this.isInPreviewMode) {
            return;
        }
        if (this.config.refreshOption === REFRESH_OPTION.HISTORY) {
            return;
        }
        if (this.config.isAutoRefreshEnabled === false) {
            return;
        }
        const current = this.contextConfig();
        this.contextConfig.set({ ...current, isAutoRefreshEnabled: !isScrolling });
        this.isLinkedToGlobal.set(!isScrolling);
    }
    /**
     * Sets up the scrolling subscription.
     *
     * Ensures similar UX as in the alarms countdown-pause logic.
     */
    setScrollingSubscription() {
        this.subscription = this.scrollingSubject$
            .pipe(debounceTime(300))
            .subscribe(value => this.isScrolling.set(value));
    }
    /**
     * Prepares the table data by:
     * - filtering out inactive data points,
     * - checking if there are multiple devices as a source of data points,
     * - getting the column headers for devices,
     * - getting the series data for active data points (API call),
     * - preparing data points with values list,
     * - mapping data points with values to list items,
     * - grouping data points by date and device,
     * - sorting data by date descending.
     *
     * @param roundSeconds - Whether to round the seconds or not.
     *                       If true, the seconds will be rounded to 0.
     *                       If false, the seconds will be displayed as they are.
     */
    async prepareTableData(roundSeconds = true) {
        this.isLoading$.next(true);
        if (this.isInPreviewMode || this.isInitialRequest) {
            if (this.config.datapoints?.length) {
                this.config.datapoints.forEach(datapoint => this.assignContextFromContextDashboard(datapoint));
            }
            this.activeDatapoints = this.datapointsTableViewService.filterOutInactiveDatapoints(this.config.datapoints);
            this.hasMultipleDatapoints = this.datapointsTableViewService.hasMultipleDatapoints(this.activeDatapoints);
            this.devicesColumnHeaders = this.datapointsTableViewService.getColumnHeaders(this.activeDatapoints);
            this.isInitialRequest = false;
        }
        const activeDatapointsIdsWithSeries = this.datapointsTableViewService.groupSeriesByDeviceId(this.activeDatapoints);
        const activeDatapointsSeriesData = await this.getActiveDatapointsSeriesDataMap(activeDatapointsIdsWithSeries, this.config, roundSeconds);
        if (this.isFailedToFetchSeriesData) {
            return;
        }
        this.seriesWithoutPermissionToRead =
            this.datapointsTableViewService.getSeriesWithoutPermissionToRead(activeDatapointsSeriesData, activeDatapointsIdsWithSeries);
        if (!this.isMissingAnyPermissionAlertShown &&
            this.seriesWithoutPermissionToRead?.length > 0 &&
            activeDatapointsSeriesData?.size) {
            this.handleMissingAnyPermissionErrorMessage();
            this.isMissingAnyPermissionAlertShown = true;
        }
        if (!activeDatapointsSeriesData) {
            return;
        }
        this.datapointsWithValues = this.datapointsTableViewService.getDatapointsWithValues(this.activeDatapoints, activeDatapointsSeriesData);
        const datapointsListItems = this.datapointsTableViewService.mapDatapointsWithValuesToList(this.datapointsWithValues);
        const groupedDatapointsListItems = this.datapointsTableViewService.groupByDateAndDevice(datapointsListItems, this.devicesColumnHeaders);
        this.datapointsTableItems = this.datapointsTableViewService.sortDataByDateDescending(groupedDatapointsListItems);
        this.isLoading$.next(false);
    }
    /**
     * Retrieves the active data points series data and returns it as a map.
     *
     * It's a wrapper method with try-catch block.
     *
     * @param datapointsIdsWithSeries - A map of data point IDs with their associated series.
     * @param config - The configuration of the data points table.
     * @param roundSeconds - Whether to round the seconds or not.
     *                       If true, the seconds will be rounded to 0.
     *                       If false, the seconds will be displayed as they are.
     * @returns A Promise that resolves to a Map object with data point IDs as keys and DataObject as values.
     */
    async getActiveDatapointsSeriesDataMap(datapointsIdsWithSeries, config, roundSeconds) {
        try {
            this.isFailedToFetchSeriesData = false;
            return await this.datapointsTableViewService.getAllActiveSeriesDataMap(datapointsIdsWithSeries, config, roundSeconds);
        }
        catch (error) {
            this.isLoading$.next(false);
            this.handleFetchError(error);
        }
    }
    handleFetchError(error) {
        // handle HTTP 422 errors (Unprocessable Entity), which occur when the user provides a series in an invalid format
        if (error['status'] === 422) {
            this.alerts.setAlertGroupDismissStrategy('warning', DismissAlertStrategy.NONE);
            this.alerts.addAlerts(new DynamicComponentAlert({
                type: 'danger',
                text: error.message,
                allowHtml: true
            }));
            this.isRefreshDisabled = true;
            return;
        }
        if (error?.message.includes('403')) {
            this.alerts.setAlertGroupDismissStrategy('warning', DismissAlertStrategy.NONE);
            this.isRefreshDisabled = true;
            return;
        }
        const isTimeoutError = error?.name === 'TimeoutError';
        const errorMessage = isTimeoutError
            ? this.TIMEOUT_ERROR_TEXT
            : (error?.message ?? this.SERVER_ERROR_TEXT);
        this.alerts.setAlertGroupDismissStrategy('warning', DismissAlertStrategy.TEMPORARY_OR_PERMANENT);
        this.alerts.addAlerts(new DynamicComponentAlert({
            type: 'warning',
            text: errorMessage
        }));
        this.isFailedToFetchSeriesData = true;
        this.alertService.addServerFailure(error);
    }
    handleMissingAnyPermissionErrorMessage() {
        this.alerts.setAlertGroupDismissStrategy('system', DismissAlertStrategy.TEMPORARY);
        this.alerts.addAlerts(new DynamicComponentAlert({
            allowHtml: true,
            text: this.getMissingPermissionsMessage(),
            type: 'system'
        }));
    }
    getMissingPermissionsMessage() {
        const baseMessage = this.translateService.instant(gettext(`You don't have permissions to read the following series:`));
        const formattedList = this.seriesWithoutPermissionToRead
            .map(datapoint => `<li>${datapoint.key.valueOf()}_${datapoint.value}</li>`)
            .join('');
        return `
      <div>
        <p>${baseMessage}</p>
        <ul>
          ${formattedList}
        </ul>
        <p><strong>Note:</strong> The name convention for each list item is: <code>[source]_[fragment.series]</code></p>
      </div>
    `;
    }
    updateExportConfig() {
        this.exportConfig = {
            aggregation: this.config.aggregation,
            dateFrom: this.config.dateTimeContext.dateFrom instanceof Date
                ? this.config.dateTimeContext.dateFrom.toISOString()
                : this.config.dateTimeContext.dateFrom,
            dateTo: this.config.dateTimeContext.dateTo instanceof Date
                ? this.config.dateTimeContext.dateTo.toISOString()
                : this.config.dateTimeContext.dateTo,
            datapointDetails: this.activeDatapoints.map(({ __target, fragment, series }) => ({
                deviceName: __target.name,
                source: __target.id,
                valueFragmentSeries: series,
                valueFragmentType: fragment
            }))
        };
    }
    checkIfHasAnyActiveDatapoint(datapoints) {
        if (!Array.isArray(datapoints) || datapoints.length === 0) {
            return false;
        }
        return !datapoints.every(dp => dp.__active === false);
    }
    assignContextFromContextDashboard(datapoint) {
        if (!this.dashboardContextComponent?.isDeviceTypeDashboard) {
            return;
        }
        const context = this.dashboardContextComponent?.context;
        if (context?.id) {
            const { name, id } = context;
            datapoint.__target = { name, id };
        }
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: DatapointsTableViewWidgetComponent, deps: [{ token: i1$2.AlertService }, { token: i1$2.DashboardChildComponent, optional: true }, { token: DatapointsTableViewService }, { token: i1$1.TranslateService }, { token: i4.ContextDashboardComponent, optional: true }, { token: i5.WidgetConfigMigrationService }], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.19", type: DatapointsTableViewWidgetComponent, isStandalone: true, selector: "c8y-datapoints-table-view", inputs: { config: "config", isInPreviewMode: "isInPreviewMode" }, host: { classAttribute: "d-col fit-h" }, usesOnChanges: true, ngImport: i0, template: "@if (!isInPreviewMode) {\n  @if (displayMode() === GLOBAL_CONTEXT_DISPLAY_MODE.DASHBOARD) {\n    <c8y-global-context-connector\n      [controls]=\"widgetControls()\"\n      [config]=\"contextConfig()\"\n      [isLoading]=\"isLoading$ | async\"\n      [dashboardChild]=\"dashboardChild\"\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$ | async\"\n      (configChange)=\"onContextChange($event)\"\n      (refresh)=\"onRefresh()\"\n    ></c8y-local-controls>\n  }\n\n  <c8y-datapoints-export-selector\n    class=\"m-l-auto p-b-8\"\n    [containerClass]=\"containerClass\"\n    [exportConfig]=\"exportConfig\"\n    (isOpen)=\"onExportModalOpen($event)\"\n  ></c8y-datapoints-export-selector>\n}\n@if (devicesColumnHeaders) {\n  <c8y-datapoints-table\n    [aggregationType]=\"config.aggregation\"\n    [datapointsTableItems]=\"datapointsTableItems\"\n    [decimalPlaces]=\"config.decimalPlaces\"\n    [devicesColumnHeaders]=\"devicesColumnHeaders\"\n    [hasMultipleDatapoints]=\"hasMultipleDatapoints\"\n    [isLoading]=\"isLoading$ | async\"\n    [seriesWithoutPermissionToReadCount]=\"seriesWithoutPermissionToRead?.length\"\n    (isScrolling)=\"onScrolling($event)\"\n  ></c8y-datapoints-table>\n}\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: DatapointsExportSelectorComponent, selector: "c8y-datapoints-export-selector", inputs: ["displayMode", "containerClass", "exportConfig"], outputs: ["isOpen"] }, { kind: "component", type: DatapointsTableComponent, selector: "c8y-datapoints-table", inputs: ["aggregationType", "datapointsTableItems", "devicesColumnHeaders", "decimalPlaces", "hasMultipleDatapoints", "isLoading", "seriesWithoutPermissionToReadCount"], outputs: ["isScrolling"] }, { kind: "component", type: GlobalContextConnectorComponent, selector: "c8y-global-context-connector", inputs: ["controls", "config", "isLoading", "dashboardChild", "linked", "emitRefresh"], outputs: ["configChange", "refresh", "linkedChange"] }, { kind: "component", type: LocalControlsComponent, selector: "c8y-local-controls", inputs: ["controls", "displayMode", "config", "isLoading", "disabled", "emitRefresh"], outputs: ["configChange", "refresh"] }, { kind: "pipe", type: AsyncPipe, name: "async" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: DatapointsTableViewWidgetComponent, decorators: [{
            type: Component,
            args: [{ selector: 'c8y-datapoints-table-view', host: { class: 'd-col fit-h' }, standalone: true, imports: [
                        AsyncPipe,
                        CommonModule,
                        DatapointsExportSelectorComponent,
                        DatapointsTableComponent,
                        GlobalContextConnectorComponent,
                        LocalControlsComponent
                    ], template: "@if (!isInPreviewMode) {\n  @if (displayMode() === GLOBAL_CONTEXT_DISPLAY_MODE.DASHBOARD) {\n    <c8y-global-context-connector\n      [controls]=\"widgetControls()\"\n      [config]=\"contextConfig()\"\n      [isLoading]=\"isLoading$ | async\"\n      [dashboardChild]=\"dashboardChild\"\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$ | async\"\n      (configChange)=\"onContextChange($event)\"\n      (refresh)=\"onRefresh()\"\n    ></c8y-local-controls>\n  }\n\n  <c8y-datapoints-export-selector\n    class=\"m-l-auto p-b-8\"\n    [containerClass]=\"containerClass\"\n    [exportConfig]=\"exportConfig\"\n    (isOpen)=\"onExportModalOpen($event)\"\n  ></c8y-datapoints-export-selector>\n}\n@if (devicesColumnHeaders) {\n  <c8y-datapoints-table\n    [aggregationType]=\"config.aggregation\"\n    [datapointsTableItems]=\"datapointsTableItems\"\n    [decimalPlaces]=\"config.decimalPlaces\"\n    [devicesColumnHeaders]=\"devicesColumnHeaders\"\n    [hasMultipleDatapoints]=\"hasMultipleDatapoints\"\n    [isLoading]=\"isLoading$ | async\"\n    [seriesWithoutPermissionToReadCount]=\"seriesWithoutPermissionToRead?.length\"\n    (isScrolling)=\"onScrolling($event)\"\n  ></c8y-datapoints-table>\n}\n" }]
        }], ctorParameters: () => [{ type: i1$2.AlertService }, { type: i1$2.DashboardChildComponent, decorators: [{
                    type: Optional
                }] }, { type: DatapointsTableViewService }, { type: i1$1.TranslateService }, { type: i4.ContextDashboardComponent, decorators: [{
                    type: Optional
                }] }, { type: i5.WidgetConfigMigrationService }], propDecorators: { config: [{
                type: Input
            }], isInPreviewMode: [{
                type: Input
            }] } });

const DEFAULT_DECIMAL_PLACES = 2;
const MIN_DECIMAL_PLACES = 0;
const MAX_DECIMAL_PLACES = 10;
class DatapointsTableWidgetConfigComponent {
    constructor() {
        this.destroyRef = inject(DestroyRef);
        this.formBuilder = inject(FormBuilder);
        this.parentForm = inject(NgForm, { optional: true });
        this.widgetConfig = inject(WidgetConfigComponent);
        this.widgetConfigService = inject(WidgetConfigService);
        this.AGGREGATION_LABELS = AGGREGATION_LABELS;
        this.DATE_SELECTION_LABELS = DATE_SELECTION_LABELS;
        this.DEFAULT_DATE_SELECTOR_VALUE = DATE_SELECTION_VALUES.dashboard_context;
        this.DEFAULT_INTERVAL_VALUE = INTERVAL_VALUES.hours;
        this.TIME_RANGE_INTERVAL_LABELS = TIME_RANGE_INTERVAL_LABELS;
        this.AGGREGATION_VALUES_ARR = AGGREGATION_VALUES_ARR;
        this.DATE_SELECTION_VALUES_ARR = DATE_SELECTION_VALUES_ARR;
        this.INTERVAL_VALUES_ARR = INTERVAL_VALUES_ARR;
        this.REFRESH_INTERVAL_VALUES_ARR = REFRESH_INTERVAL_VALUES_ARR;
        this.datapointSelectionConfig = {};
        this.disabledAggregationOptions = {};
        this.defaultFormOptions = {
            selectableChartLineTypes: [],
            selectableAxisTypes: [],
            showRedRange: true,
            showYellowRange: true
        };
        this.widgetControls = PRESET_NAME.DATA_TABLE;
        /**
         * Debounced config for preview to prevent multiple series requests on initial load.
         * Uses auditTime to batch rapid emissions (e.g., from initConfig + GlobalContext processing).
         * Without it the preview may request data multiple times unnecessarily.
         */
        this.previewConfig$ = this.widgetConfigService.currentConfig$.pipe(auditTime(350));
        this.DATAPOINTS_TABLE_CONTROL_NAME = 'datapointsTableControl';
    }
    set previewMapSet(template) {
        this.widgetConfigService.setPreview(template ?? null);
    }
    ngOnInit() {
        if (this.widgetConfig.context?.id) {
            this.datapointSelectionConfig.contextAsset = this.widgetConfig?.context;
        }
        this.initForm();
    }
    ngOnDestroy() {
        this.parentForm.form.removeControl(this.DATAPOINTS_TABLE_CONTROL_NAME);
    }
    onBeforeSave(config) {
        if (this.formGroup.valid) {
            Object.assign(config, this.formGroup.value);
            return true;
        }
        return false;
    }
    initForm() {
        this.formGroup = this.createForm();
        this.parentForm.form.addControl(this.DATAPOINTS_TABLE_CONTROL_NAME, this.formGroup);
        this.subscribeToFormChanges();
    }
    subscribeToFormChanges() {
        this.formGroup.valueChanges
            .pipe(debounceTime$1(150), takeUntilDestroyed(this.destroyRef))
            .subscribe(formValue => {
            this.widgetConfigService.updateConfig(formValue);
        });
    }
    createForm() {
        return this.formBuilder.group({
            decimalPlaces: [
                this.config.decimalPlaces ?? DEFAULT_DECIMAL_PLACES,
                [
                    Validators.required,
                    Validators.min(MIN_DECIMAL_PLACES),
                    Validators.max(MAX_DECIMAL_PLACES),
                    Validators.pattern('^[0-9]+$')
                ]
            ]
        });
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: DatapointsTableWidgetConfigComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.19", type: DatapointsTableWidgetConfigComponent, isStandalone: true, selector: "c8y-datapoints-table-view-config", inputs: { config: "config" }, viewQueries: [{ propertyName: "previewMapSet", first: true, predicate: ["datapointsTablePreview"], descendants: true }], ngImport: i0, template: "<div class=\"p-l-24 p-r-24\">\n  @if (formGroup) {\n    <form [formGroup]=\"formGroup\">\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          />\n        </c8y-form-group>\n      </fieldset>\n    </form>\n  }\n</div>\n\n<ng-template #datapointsTablePreview>\n  @let previewConfig = previewConfig$ | async;\n\n  @if (previewConfig?.datapoints?.length > 0) {\n    @if (previewConfig?.displayMode !== 'dashboard') {\n      <c8y-local-controls\n        [controls]=\"widgetControls\"\n        [displayMode]=\"previewConfig?.displayMode\"\n        [config]=\"previewConfig\"\n        [disabled]=\"true\"\n      ></c8y-local-controls>\n    }\n    <c8y-datapoints-table-view\n      [config]=\"previewConfig\"\n      [isInPreviewMode]=\"true\"\n    ></c8y-datapoints-table-view>\n  } @else {\n    <div class=\"col-md-6 d-col a-i-start j-c-center\">\n      <c8y-ui-empty-state\n        [icon]=\"'c8y-data-points'\"\n        [title]=\"'No data points selected' | translate\"\n        [subtitle]=\"'Select data point to render content' | translate\"\n        [horizontal]=\"false\"\n        data-cy=\"datapoints-table-list--empty-state-no-data-point-selected\"\n      >\n        <p c8y-guide-docs>\n          <small\n            translate\n            ngNonBindable\n          >\n            Find out more in the\n            <a c8y-guide-href=\"/docs/cockpit/widgets-collection/#data-point-table\">\n              user documentation</a\n            >.\n          </small>\n        </p>\n      </c8y-ui-empty-state>\n    </div>\n  }\n</ng-template>\n", dependencies: [{ kind: "ngmodule", type: CoreModule }, { kind: "component", type: i1$2.EmptyStateComponent, selector: "c8y-ui-empty-state", inputs: ["icon", "title", "subtitle", "horizontal"] }, { kind: "directive", type: i1$2.C8yTranslateDirective, selector: "[translate],[ngx-translate]" }, { kind: "directive", type: i2$1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i2$1.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$1.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i2$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "component", type: i1$2.FormGroupComponent, selector: "c8y-form-group", inputs: ["hasError", "hasWarning", "hasSuccess", "novalidation", "status"] }, { kind: "directive", type: i1$2.RequiredInputPlaceholderDirective, selector: "input[required], input[formControlName]" }, { kind: "directive", type: i1$2.GuideHrefDirective, selector: "[c8y-guide-href]", inputs: ["c8y-guide-href"] }, { kind: "component", type: i1$2.GuideDocsComponent, selector: "[c8y-guide-docs]" }, { kind: "directive", type: i2$1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i2$1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: DatapointSelectorModule }, { kind: "component", type: DatapointsTableViewWidgetComponent, selector: "c8y-datapoints-table-view", inputs: ["config", "isInPreviewMode"] }, { kind: "component", type: LocalControlsComponent, selector: "c8y-local-controls", inputs: ["controls", "displayMode", "config", "isLoading", "disabled", "emitRefresh"], outputs: ["configChange", "refresh"] }, { kind: "ngmodule", type: PopoverModule }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "pipe", type: i1$2.C8yTranslatePipe, name: "translate" }, { kind: "pipe", type: i2.AsyncPipe, name: "async" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: DatapointsTableWidgetConfigComponent, decorators: [{
            type: Component,
            args: [{ selector: 'c8y-datapoints-table-view-config', standalone: true, imports: [
                        CoreModule,
                        DatapointSelectorModule,
                        DatapointsTableViewWidgetComponent,
                        LocalControlsComponent,
                        PopoverModule,
                        ReactiveFormsModule
                    ], template: "<div class=\"p-l-24 p-r-24\">\n  @if (formGroup) {\n    <form [formGroup]=\"formGroup\">\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          />\n        </c8y-form-group>\n      </fieldset>\n    </form>\n  }\n</div>\n\n<ng-template #datapointsTablePreview>\n  @let previewConfig = previewConfig$ | async;\n\n  @if (previewConfig?.datapoints?.length > 0) {\n    @if (previewConfig?.displayMode !== 'dashboard') {\n      <c8y-local-controls\n        [controls]=\"widgetControls\"\n        [displayMode]=\"previewConfig?.displayMode\"\n        [config]=\"previewConfig\"\n        [disabled]=\"true\"\n      ></c8y-local-controls>\n    }\n    <c8y-datapoints-table-view\n      [config]=\"previewConfig\"\n      [isInPreviewMode]=\"true\"\n    ></c8y-datapoints-table-view>\n  } @else {\n    <div class=\"col-md-6 d-col a-i-start j-c-center\">\n      <c8y-ui-empty-state\n        [icon]=\"'c8y-data-points'\"\n        [title]=\"'No data points selected' | translate\"\n        [subtitle]=\"'Select data point to render content' | translate\"\n        [horizontal]=\"false\"\n        data-cy=\"datapoints-table-list--empty-state-no-data-point-selected\"\n      >\n        <p c8y-guide-docs>\n          <small\n            translate\n            ngNonBindable\n          >\n            Find out more in the\n            <a c8y-guide-href=\"/docs/cockpit/widgets-collection/#data-point-table\">\n              user documentation</a\n            >.\n          </small>\n        </p>\n      </c8y-ui-empty-state>\n    </div>\n  }\n</ng-template>\n" }]
        }], propDecorators: { previewMapSet: [{
                type: ViewChild,
                args: ['datapointsTablePreview']
            }], config: [{
                type: Input
            }] } });

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

export { AdjustAggregatedTimeRangePipe, DATE_SELECTION_LABELS, DATE_SELECTION_VALUES, DATE_SELECTION_VALUES_ARR, DEFAULT_DPT_REFRESH_INTERVAL_VALUE, DURATION_OPTIONS, DatapointsTableComponent, DatapointsTableViewService, DatapointsTableViewWidgetComponent, DatapointsTableWidgetConfigComponent, INTERVAL_VALUES_ARR, REFRESH_INTERVAL_VALUES_ARR, RENDER_TYPES_LABELS, TIME_RANGE_INTERVAL_LABELS };
//# sourceMappingURL=c8y-ngx-components-widgets-implementations-datapoints-table.mjs.map
