UNPKG

@c8y/ngx-components

Version:

Angular modules for Cumulocity IoT applications

4,184 lines 260 kB
import * as i0 from '@angular/core';
import { Injectable, Component, inject, InjectionToken, EventEmitter, Output, Input, TemplateRef, ViewChild, Optional, Inject, HostListener, HostBinding, NgModule } from '@angular/core';
import * as i2 from '@c8y/ngx-components';
import { gettext, IconDirective, C8yTranslatePipe, Permissions, ContextRouteService, Status, NavigatorNode, NEW_DASHBOARD_ROUTER_STATE_PROP, getActivatedRoute, ViewContext, TabsOutletComponent, memoize, hookGeneric, ExtensionPointForPlugins, fromTriggerOnce, getInjectedHooks, stateToFactory, ListGroupModule, C8yComponentOutlet, AlertService, DynamicComponentModule, DynamicComponentComponent, sortByPriority, isPromise, CoreModule, C8yTranslateDirective, HighlightComponent, EmptyStateComponent, FormGroupComponent, RequiredInputPlaceholderDirective, ProductExperienceDirective, DashboardChildChange, CopyDashboardDisabledReason, TitleComponent, ActionBarItemComponent, HelpComponent, WidgetsDashboardComponent, DatePipe, hookRoute, hookTab, hookNavigator } from '@c8y/ngx-components';
import * as i1 from '@angular/router';
import { RouterOutlet, ActivatedRoute, RouterModule } from '@angular/router';
import { of, Subject, from, combineLatest, map as map$1, timer, BehaviorSubject, tap as tap$1, withLatestFrom, mergeMap as mergeMap$1, shareReplay as shareReplay$1, merge, scan, debounceTime, isObservable, iif } from 'rxjs';
import { __decorate, __metadata } from 'tslib';
import * as i2$1 from '@ngx-translate/core';
import { assign, pick, isEmpty, cloneDeep, some, keys, keyBy, has, set, reduce, forEach, get, isEqual, clone, omit, sortBy, escapeRegExp, findIndex, kebabCase, every } from 'lodash-es';
import * as i1$1 from '@c8y/client';
import { QueriesUtil, InventoryService } from '@c8y/client';
import { tap, map, catchError, throwIfEmpty, filter, mergeMap, toArray, first, take, distinctUntilChanged, takeUntil, shareReplay, switchMap } from 'rxjs/operators';
import * as i5 from '@angular/forms';
import { Validators, NgForm, ControlContainer, FormsModule } from '@angular/forms';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import * as i2$3 from 'ngx-bootstrap/collapse';
import { CollapseDirective, CollapseModule } from 'ngx-bootstrap/collapse';
import * as i3 from '@angular/common';
import { NgIf, NgTemplateOutlet, CommonModule, AsyncPipe, NgClass, NgFor, NgForOf } from '@angular/common';
import * as i1$2 from '@c8y/ngx-components/assets-navigator';
import { AssetSelectorModule } from '@c8y/ngx-components/assets-navigator';
import * as i2$2 from 'ngx-bootstrap/popover';
import { PopoverModule, PopoverDirective } from 'ngx-bootstrap/popover';
import { IconSelectorModule } from '@c8y/ngx-components/icon-selector';
import * as i1$3 from 'ngx-bootstrap/dropdown';
import { BsDropdownModule } from 'ngx-bootstrap/dropdown';
import { TooltipModule } from 'ngx-bootstrap/tooltip';
import { defaultWidgetIds } from '@c8y/ngx-components/widgets/definitions';

const newDashboardTab = {
    featureId: 'newDashboard',
    icon: 'th',
    label: gettext('New dashboard'),
    path: 'new-dashboard',
    // place tab as last one and hide it so it won't be opened until user initiates adding new dashboard
    hide: true,
    priority: -Infinity
};
class NewDashboardGuard {
    canActivate(route) {
        const tabActive = route.routeConfig.path === newDashboardTab.path;
        if (tabActive) {
            const dashboard = {
                c8y_Dashboard: null
            };
            route.data = { dashboard };
        }
        if (!this.tab) {
            this.tab = {
                ...newDashboardTab,
                hide: !tabActive,
                priority: tabActive ? Infinity : -Infinity
            };
        }
        return of([this.tab]);
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: NewDashboardGuard, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
    static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: NewDashboardGuard, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: NewDashboardGuard, decorators: [{
            type: Injectable,
            args: [{ providedIn: 'root' }]
        }] });

class AddDashboardComponent {
    constructor(tabsService, router) {
        this.tabsService = tabsService;
        this.router = router;
    }
    addDashboard() {
        const tempNewDashboardTab = [...this.tabsService.state].find(t => t.featureId === newDashboardTab.featureId);
        // navigate before tab is displayed, because in DashboardDetailComponent tab is hidden on navigation from it.
        this.router.navigate(typeof tempNewDashboardTab.path === 'string'
            ? [tempNewDashboardTab.path]
            : tempNewDashboardTab.path, { replaceUrl: true });
        // show tab and make it appear as first one
        tempNewDashboardTab.hide = false;
        tempNewDashboardTab.priority = Infinity;
        this.tabsService.refresh();
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AddDashboardComponent, deps: [{ token: i2.TabsService }, { token: i1.Router }], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.14", type: AddDashboardComponent, isStandalone: true, selector: "[c8y-add-dashboard]", host: { classAttribute: "d-flex a-i-stretch sticky-right" }, ngImport: i0, template: "<div class=\"d-flex a-i-stretch m-b-8 m-t-8 p-l-8 hidden-xs\">\n  <button\n    class=\"btn btn-default btn-sm p-l-8 p-r-8 fit-h p-b-0 p-t-0 d-flex a-i-center\"\n    title=\"{{ 'Add dashboard' | translate }}\"\n    type=\"button\"\n    (click)=\"addDashboard()\"\n  >\n    <i\n      class=\"icon-20 m-r-4\"\n      c8yIcon=\"add-circle-outline\"\n    ></i>\n    <span>{{ 'Add dashboard' | translate }}</span>\n  </button>\n  <div class=\"p-r-sm-40\"></div>\n</div>\n", dependencies: [{ kind: "directive", type: IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "pipe", type: C8yTranslatePipe, name: "translate" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AddDashboardComponent, decorators: [{
            type: Component,
            args: [{ selector: '[c8y-add-dashboard]', host: { class: 'd-flex a-i-stretch sticky-right' }, imports: [IconDirective, C8yTranslatePipe], template: "<div class=\"d-flex a-i-stretch m-b-8 m-t-8 p-l-8 hidden-xs\">\n  <button\n    class=\"btn btn-default btn-sm p-l-8 p-r-8 fit-h p-b-0 p-t-0 d-flex a-i-center\"\n    title=\"{{ 'Add dashboard' | translate }}\"\n    type=\"button\"\n    (click)=\"addDashboard()\"\n  >\n    <i\n      class=\"icon-20 m-r-4\"\n      c8yIcon=\"add-circle-outline\"\n    ></i>\n    <span>{{ 'Add dashboard' | translate }}</span>\n  </button>\n  <div class=\"p-r-sm-40\"></div>\n</div>\n" }]
        }], ctorParameters: () => [{ type: i2.TabsService }, { type: i1.Router }] });

class AddDashboardFactory {
    constructor() {
        this.permissions = inject(Permissions);
        this.contextRoute = inject(ContextRouteService);
    }
    async get(activatedRoute) {
        this.currentContext = this.contextRoute.getContextData(activatedRoute);
        if (this.currentContext?.context === this.targetContext &&
            (await this.hasPermission(this.currentContext.contextData))) {
            return [
                {
                    component: AddDashboardComponent,
                    priority: -Infinity,
                    showAlways: true
                }
            ];
        }
        return [];
    }
    async hasPermission(context) {
        if (context?.id) {
            return await this.permissions.canEdit([
                Permissions.ROLE_INVENTORY_ADMIN,
                Permissions.ROLE_INVENTORY_CREATE,
                Permissions.ROLE_MANAGED_OBJECT_ADMIN,
                Permissions.ROLE_MANAGED_OBJECT_CREATE
            ], context);
        }
        return this.permissions.hasAnyRole([
            Permissions.ROLE_INVENTORY_ADMIN,
            Permissions.ROLE_INVENTORY_CREATE,
            Permissions.ROLE_MANAGED_OBJECT_ADMIN,
            Permissions.ROLE_MANAGED_OBJECT_CREATE
        ]);
    }
}

const CONTEXT_DASHBOARD_CONFIG = new InjectionToken('ContextDashboardConfig');
const DASHBOARD_SETTINGS_CHANGES = {
    classes: gettext('theme'),
    globalRolesIds: gettext('global roles'),
    widgetClasses: gettext('widget header style'),
    widgetMargin: gettext('widget margin'),
    icon: gettext('icon'),
    name: gettext('name'),
    priority: gettext('priority'),
    c8y_IsNavigatorNode: gettext('navigator item'),
    translateWidgetTitle: gettext('translate widget title'),
    children: gettext('widgets')
};
const DASHBOARD_CHILDREN_STATE_NAME = {
    initial: gettext('Initial state'),
    config: gettext('Widget configuration changed'),
    removed: gettext('Widget removed'),
    added: gettext('Widget added'),
    arrangement: gettext('Widgets rearranged')
};
var ContextDashboardType;
(function (ContextDashboardType) {
    ContextDashboardType["Device"] = "device";
    ContextDashboardType["Type"] = "type";
    ContextDashboardType["Group"] = "group";
    ContextDashboardType["Named"] = "name";
    ContextDashboardType["Report"] = "report";
})(ContextDashboardType || (ContextDashboardType = {}));
var DashboardDetailsTabId;
(function (DashboardDetailsTabId) {
    DashboardDetailsTabId["GENERAL"] = "general";
    DashboardDetailsTabId["APPEARANCE"] = "appearance";
    DashboardDetailsTabId["VERSIONHISTORY"] = "versionHistory";
})(DashboardDetailsTabId || (DashboardDetailsTabId = {}));
const WIDGET_HEADER_CLASSES = [
    {
        label: gettext('Regular`style`'),
        class: 'panel-title-regular',
        description: gettext('The widget has no border between header and content.')
    },
    {
        label: gettext('Border`style`'),
        class: 'panel-title-border',
        description: gettext('The widget has a small separation border between header and content.')
    },
    {
        label: gettext('Overlay`style`'),
        class: 'panel-title-overlay',
        description: gettext('The widget content overlays the header.')
    },
    {
        label: gettext('Hidden`style`'),
        class: 'panel-title-hidden',
        description: gettext('The widget header is not shown.')
    }
];
const WIDGET_CONTENT_CLASSES = [
    {
        label: gettext('Branded`style`'),
        class: 'panel-content-branded',
        description: gettext('The widget is styled with the main brand color.')
    },
    {
        label: gettext('Match dashboard`style`'),
        class: 'panel-content-light',
        description: gettext('The widget appearance matches the dashboard appearance.')
    },
    {
        label: gettext('Light`style`'),
        class: 'panel-content-white',
        description: gettext('The widget has light appearance, that is, dark text on light background.')
    },
    {
        label: gettext('Dark`style`'),
        class: 'panel-content-dark',
        description: gettext('The widget has dark appearance, that is, light text on dark background.')
    },
    {
        label: gettext('Transparent`style`'),
        class: 'panel-content-transparent',
        description: gettext('The widget has no background.')
    }
];
const DASHBOARD_THEME_CLASSES = [
    {
        label: gettext('Match UI`theme`'),
        class: 'dashboard-theme-light',
        description: gettext('The dashboard appearance matches the UI appearance.')
    },
    {
        label: gettext('Light`theme`'),
        class: 'dashboard-theme-white',
        description: gettext('The dashboard has light appearance, that is, dark text on light background.')
    },
    {
        label: gettext('Dark`theme`'),
        class: 'dashboard-theme-dark',
        description: gettext('The dashboard has dark appearance, that is, light text on dark background.')
    },
    {
        label: gettext('Branded`theme`'),
        class: 'dashboard-theme-branded',
        description: gettext('The dashboard is styled using the brand palette.')
    }
];
const STYLING_CLASS_PREFIXES = [
    'dashboard-theme-',
    'panel-title-',
    'panel-content-'
];
const ALL_GLOBAL_ROLES_SELECTED = 'all';
const PRODUCT_EXPERIENCE = {
    DASHBOARD: {
        EVENTS: {
            DASHBOARDS: 'dashboards',
            REPORTS: 'reports',
            DASHBOARD_TEMPLATE: 'dashboardTemplate'
        },
        COMPONENTS: {
            DASHBOARD_VIEW: 'context-dashboard',
            DASHBOARD_AVAILABILITY: 'dashboard-availability',
            REPORTS_LIST: 'report-dashboard-list',
            ADD_REPORT: 'report-dashboard-list',
            ADD_DASHBOARD: 'add-dashboard',
            DELETE_DASHBOARD: 'context-dashboard',
            TYPED_DASHBOARD_SETTINGS: 'typed-dashboard-settings'
        },
        CONTEXT: {
            REPORT: 'report',
            DEVICE: 'device',
            ASSET: 'asset',
            GROUP: 'group'
        },
        ACTIONS: {
            APPLY_GLOBAL_ROLES_CHANGES: 'applyGlobalRolesChanges',
            DELETE: 'delete',
            LOAD: 'load',
            CREATE: 'create',
            ADD_REPORT: 'addReport',
            DUPLICATE_AS_REGULAR_DASHBOARD: 'duplicateAsRegularDashboard'
        }
    }
};
const REPORT_DEFAULT_NAVIGATION_NODE_PRIORITY = 30;
const DASHBOARD_DETAILS_OUTLET = 'dashboard-details';
const DASHBOARD_DETAILS_TABS_OUTLET_NAME = 'dashboardTabs';

class ContextDashboardService {
    get formDisabled() {
        return this._formDisabled;
    }
    set formDisabled(value) {
        this._formDisabled = value;
        this.formDisabledSubject.next(value);
    }
    constructor(inventory, tabs, modal, translateService, router, navigator, permissions, alert, dynamicComponent, groupService, optionsService) {
        this.inventory = inventory;
        this.tabs = tabs;
        this.modal = modal;
        this.translateService = translateService;
        this.router = router;
        this.navigator = navigator;
        this.permissions = permissions;
        this.alert = alert;
        this.dynamicComponent = dynamicComponent;
        this.groupService = groupService;
        this.optionsService = optionsService;
        this.REPORT_PARTIAL_NAME = 'report_';
        this.VERSION_HISTORY_SIZE_LIMIT = 10;
        this.INVENTORY_ROLES = [
            Permissions.ROLE_INVENTORY_ADMIN,
            Permissions.ROLE_MANAGED_OBJECT_ADMIN
        ];
        this.cache = new Map();
        this.DEFAULT_PAGESIZE = 1000;
        this.FRAGMENT_NAME = 'c8y_Dashboard';
        this.DASHBOARD_ROUTE_PATH = 'dashboard';
        this.INDEX_SPLIT = '!';
        this.CACHE_TIMEOUT = 500;
        this._formDisabled = true;
        this.formDisabledSubject = new Subject();
        this.HIDE_TYPE_DASHBOARD_FOR_ASSETS = 'hideTypeDashboardForAssets';
        this.formDisabled$ = this.formDisabledSubject.asObservable();
        this.queriesUtil = new QueriesUtil();
    }
    async create(dashboardCfg, context, name = '') {
        let dashboard = {};
        assign(dashboard, this.adjustDashboardFor24Columns({ c8y_Dashboard: dashboardCfg }), this.updateDashboardHistory(dashboard, dashboardCfg));
        const [dashboardType, dashboardFragments] = this.getDashboardFragments({ c8y_Dashboard: dashboardCfg }, context, name, false);
        dashboard = { ...dashboard, ...dashboardFragments };
        if (this.shouldSetGlobal(dashboard, context)) {
            assign(dashboard, { c8y_Global: {} });
        }
        dashboard.name = dashboard.c8y_Dashboard.name;
        const { data } = dashboardType === ContextDashboardType.Group ||
            dashboardType === ContextDashboardType.Device ||
            (context?.contextData?.id && dashboardType === ContextDashboardType.Named)
            ? await this.inventory.childAdditionsCreate(dashboard, context?.contextData.id || '')
            : await this.inventory.create(dashboard);
        return data;
    }
    async detail(dashboardMO) {
        let { data } = await this.inventory.detail(dashboardMO);
        data = this.adjustDashboardFor24Columns(data);
        this.cache.set(dashboardMO.id, data);
        return data;
    }
    async update(dashboard, context) {
        const dashboardCfg = dashboard.c8y_Dashboard;
        dashboard.name = dashboard.c8y_Dashboard.name;
        assign(dashboard, this.adjustDashboardFor24Columns({ c8y_Dashboard: dashboardCfg }), this.updateDashboardHistory(dashboard, dashboardCfg));
        const keepFragments = this.clean(pick(dashboard, [this.FRAGMENT_NAME, 'id', 'name']));
        keepFragments.c8y_DashboardHistory = dashboard.c8y_DashboardHistory;
        await this.serializeWidgetConfigs(keepFragments);
        const [, dashboardTypeFragments] = this.getDashboardFragments(dashboard, context, '', true);
        keepFragments.c8y_Global = this.shouldSetGlobal({ ...dashboard, ...dashboardTypeFragments });
        const { data } = await this.inventory.update({ ...keepFragments, ...dashboardTypeFragments });
        this.cache.set(dashboard.id, data);
        return data;
    }
    async delete(dashboard, withConfirmation = true) {
        try {
            if (withConfirmation) {
                let msg = gettext(`You are about to delete the dashboard "{{ dashboardName }}". Do you want to proceed?`);
                if (this.isDeviceType(dashboard)) {
                    msg = gettext(`You are about to delete the dashboard "{{ dashboardName }}" from all devices of the type "{{ deviceType }}".
           Do you want to proceed?`);
                }
                await this.modal.confirm(gettext('Delete dashboard'), this.translateService.instant(msg, {
                    dashboardName: dashboard.c8y_Dashboard.name,
                    deviceType: dashboard.c8y_Dashboard.deviceTypeValue
                }), Status.DANGER, { ok: gettext('Delete'), cancel: gettext('Cancel') });
            }
            await this.inventory.delete(dashboard);
            const tabToRemove = Array.from(this.tabs.state).find(tab => {
                if (typeof tab.path === 'string') {
                    return tab.path.endsWith(`${this.DASHBOARD_ROUTE_PATH}/${dashboard.id}`);
                }
            });
            this.tabs.remove(tabToRemove);
            queueMicrotask(() => {
                this.tabs.refresh();
            });
        }
        catch (ex) {
            // intended empty
        }
    }
    updateDashboardHistory(dashboard, dashboardCfg) {
        if (!dashboard.c8y_DashboardHistory) {
            dashboard.c8y_DashboardHistory = [];
        }
        if (isEmpty(dashboardCfg?.historyDescription)) {
            dashboardCfg.historyDescription = { changeType: 'create' };
        }
        dashboardCfg.created = new Date().toISOString();
        dashboard.c8y_DashboardHistory = cloneDeep([dashboardCfg, ...dashboard.c8y_DashboardHistory]);
        if (dashboard.c8y_DashboardHistory.length > this.VERSION_HISTORY_SIZE_LIMIT) {
            dashboard.c8y_DashboardHistory = [
                ...dashboard.c8y_DashboardHistory.slice(0, this.VERSION_HISTORY_SIZE_LIMIT)
            ];
        }
        return dashboard;
    }
    activateDashboards(route, types) {
        const { dashboardId } = route.params;
        if (dashboardId) {
            return this.getDashboard$(dashboardId, types, route.parent.data.contextData).pipe(tap(dashboard => {
                route.data = { dashboard };
            }), map(() => true), catchError(() => {
                return of(false);
            }));
        }
        this.dashboardTabs$ = this.getTabs$(route.data.contextData, types, route?.parent?.data);
        return this.dashboardTabs$;
    }
    getDashboard(name, defaultWidgets) {
        const children = this.mapWidgets(defaultWidgets);
        return this.getDashboard$(name, [ContextDashboardType.Named]).pipe(throwIfEmpty(), catchError(() => {
            if (!this.hasPermissionsToCopyDashboard()) {
                this.alert.warning(gettext('You are viewing a read-only dashboard because you don’t have the necessary permissions to modify it.'));
            }
            return of(this.getDefaultDashboard({
                name,
                children,
                widgetClasses: { 'dashboard-theme-light': true, 'panel-title-regular': true }
            }));
        }));
    }
    /**
     * @deprecated Use `getDashboard()` instead.
     * This function will be removed in the future.
     */
    getNamedDashboardOrCreate(name, defaultWidgets, context) {
        const children = this.mapWidgets(defaultWidgets);
        return this.getDashboard$(name, [ContextDashboardType.Named]).pipe(throwIfEmpty(), catchError(() => {
            if (!this.hasPermissionsToCopyDashboard()) {
                this.alert.warning(gettext('You are viewing a read-only dashboard because you don’t have the necessary permissions to modify it.'));
                return of(this.getDefaultDashboard({
                    name,
                    children,
                    widgetClasses: { 'dashboard-theme-light': true, 'panel-title-regular': true }
                }));
            }
            else
                return from(this.create({
                    children,
                    widgetClasses: { 'dashboard-theme-light': true, 'panel-title-regular': true }
                }, context, name));
        }));
    }
    updateNavigatorItem(mo) {
        this.navigator.state.forEach(node => {
            if (node.path === `reports/${mo.id}`) {
                this.navigator.remove(node);
            }
        });
        if (mo.c8y_IsNavigatorNode) {
            const nodeToAdd = new NavigatorNode({
                label: mo.name,
                path: `reports/${mo.id}`,
                icon: mo.icon,
                priority: mo.priority
            });
            this.navigator.add(nodeToAdd);
        }
    }
    async navigateToDashboard(dashboardMO, isNewDashboard = false) {
        if (/\/dashboard\//.test(this.router.url)) {
            this.router.navigate(['..', dashboardMO.id], {
                relativeTo: getActivatedRoute(this.router),
                ...(isNewDashboard && {
                    state: { [NEW_DASHBOARD_ROUTER_STATE_PROP]: true }
                })
            });
        }
        else if (/^\/(device|group)\/[0-9]+$/.test(this.router.url)) {
            // in case the add dashboard button is the only tab on that route
            this.router.navigate(['.', this.DASHBOARD_ROUTE_PATH, dashboardMO.id], {
                relativeTo: getActivatedRoute(this.router),
                ...(isNewDashboard && {
                    state: { [NEW_DASHBOARD_ROUTER_STATE_PROP]: true }
                })
            });
        }
        else if (/^\/(device|group)\/[0-9]+\/device-info$/.test(this.router.url)) {
            this.router.navigate(['.'], {
                relativeTo: getActivatedRoute(this.router),
                ...(isNewDashboard && {
                    state: { [NEW_DASHBOARD_ROUTER_STATE_PROP]: true }
                })
            });
        }
        else {
            this.router.navigate(['..', this.DASHBOARD_ROUTE_PATH, dashboardMO.id], {
                relativeTo: getActivatedRoute(this.router),
                ...(isNewDashboard && {
                    state: { [NEW_DASHBOARD_ROUTER_STATE_PROP]: true }
                })
            });
        }
    }
    /**
     * Checks if user is able to edit dashboard according to his roles and dashboard ownership.
     *
     * @param mo - Dashboard managed object.
     * @returns True if user is able to edit dashboard, false if he cannot.
     */
    async canEditDashboard(mo) {
        return await this.permissions.canEdit(this.INVENTORY_ROLES, mo);
    }
    /**
     * Checks if user has permissions to copy dashboard according to his roles.
     *
     * @returns True if user has permissions to copy dashboard, false if he cannot.
     */
    hasPermissionsToCopyDashboard() {
        return this.permissions.hasAnyRole([
            Permissions.ROLE_INVENTORY_ADMIN,
            Permissions.ROLE_INVENTORY_CREATE,
            Permissions.ROLE_MANAGED_OBJECT_ADMIN,
            Permissions.ROLE_MANAGED_OBJECT_CREATE
        ]);
    }
    isNamed(dashboard) {
        return some(keys(dashboard), prop => new RegExp(`^${this.FRAGMENT_NAME}${this.INDEX_SPLIT}${ContextDashboardType.Named}${this.INDEX_SPLIT}`).test(prop));
    }
    isReport(dashboard) {
        return some(keys(dashboard), prop => new RegExp(`^${this.FRAGMENT_NAME}${this.INDEX_SPLIT}${ContextDashboardType.Named}${this.INDEX_SPLIT}${this.REPORT_PARTIAL_NAME}`).test(prop));
    }
    isDeviceType(dashboard) {
        return some(keys(dashboard), prop => {
            const matchingProp = new RegExp(`^${this.FRAGMENT_NAME}${this.INDEX_SPLIT}${ContextDashboardType.Type}${this.INDEX_SPLIT}`).test(prop);
            if (!matchingProp) {
                return false;
            }
            else {
                // there might be matching key, but its value can be {} or null
                return !!dashboard[prop];
            }
        });
    }
    isDeviceDashboard(dashboard) {
        return some(keys(dashboard), prop => new RegExp(`^${this.FRAGMENT_NAME}${this.INDEX_SPLIT}${ContextDashboardType.Device}${this.INDEX_SPLIT}`).test(prop));
    }
    isGroupDashboard(dashboard) {
        return some(keys(dashboard), prop => new RegExp(`^${this.FRAGMENT_NAME}${this.INDEX_SPLIT}${ContextDashboardType.Group}${this.INDEX_SPLIT}`).test(prop));
    }
    getFilteredDashboardStyles(styleList) {
        return styleList.filter(c => STYLING_CLASS_PREFIXES.some(classPrefix => c.startsWith(classPrefix)));
    }
    getStyling(styleList, styleName, defaultValue) {
        const styling = styleList.find(style => style && new RegExp(`-${styleName}$`, 'i').test(style.class));
        return styling ? styling.class : defaultValue;
    }
    mapWidgets(widgets) {
        return keyBy(widgets.map(widget => {
            widget.id = String(Math.random()).substr(2);
            return widget;
        }), 'id');
    }
    getDashboard$(dashboardIdOrName, dashboardType, mo) {
        const cache = this.cache.get(dashboardIdOrName);
        const dashboards = mo
            ? this.getContextDashboards(mo, dashboardType)
            : this.getNamedDashboard(dashboardIdOrName);
        const cacheRefresh = this.getContextDashboards$(dashboards).pipe(tap(dashboard => this.cacheDashboard(dashboard)), filter(dashboard => dashboard.id === dashboardIdOrName ||
            has(dashboard, `${this.FRAGMENT_NAME}${this.INDEX_SPLIT}${ContextDashboardType.Named}${this.INDEX_SPLIT}${dashboardIdOrName}`)));
        return cache ? of(cache) : cacheRefresh;
    }
    async pasteDashboard(newContext) {
        if (this.copyClipboard) {
            try {
                const dashboardToPaste = this.createContextDashboardCopy(this.copyClipboard.dashboard, newContext.contextData, this.copyClipboard.context.contextData);
                const dashboard = await this.create(this.clean(dashboardToPaste), newContext);
                // linking childAdditions for e.g. to grant access to the images uploaded by the image widget for users with only inventory roles.
                const { data: childAdditions } = await this.inventory.childAdditionsList(this.copyClipboard.dashboardId, { pageSize: 2000 });
                if (childAdditions.length) {
                    await this.inventory.childAdditionsBulkAdd(childAdditions, dashboard.id);
                }
                this.copyClipboard = undefined;
                this.navigateToDashboard(dashboard);
            }
            catch {
                this.alert.warning(gettext('Insufficient permissions for this action.'));
            }
        }
    }
    /**
     * Creates fragment that associates dashboards with device/asset. It consists of three elements:
     * - FRAGMENT_NAME - static string
     * - dashboard type (e.g. 'group', 'device')
     * - fragment value ( id of device/asset if it is not typed dashboard; deviceTypeValue property of dashboard if it is type dashboard)
     * Example fragment for device dashboard: 'c8y_Dashboard!device!773200'
     * Example fragment for group dashboard: 'c8y_Dashboard!group!84129208'
     * Example fragment for typed device dashboard: 'c8y_Dashboard!type!c8y_lwm2m_connector_device'
     *
     * @param contextDashboardType Type of dashboard
     * @param value Fragment value
     * @returns Fragment for dashboard
     */
    createFragmentKey(contextDashboardType, value) {
        return `${this.FRAGMENT_NAME}${this.INDEX_SPLIT}${contextDashboardType}${this.INDEX_SPLIT}${value}`;
    }
    /**
     * Indicates if dashboard can be set to type dashboard.
     * First, it checks if deviceTypeValue exists and if user has permission to set dashboard type.
     * Then, case from sensor app is checked- dashboard created with sensor app has deviceType set to true but
     * type fragment is missing- we do not support this combination.
     * @param mo Dashboard managed object
     * @param context {ContextData} Current context
     * @returns True if dashboard can be set to type dashboard, false if it is forbidden.
     */
    shouldAllowToSetDashboardType(mo, context) {
        // disallow if dashboard managed object or context is missing or context is not device/asset/group
        if (!mo ||
            !context?.contextData ||
            (context.context !== ViewContext.Device && context.context !== ViewContext.Group)) {
            return 'disallow';
        }
        // if context is asset/group and type dashboard feature is hidden for assets/groups or asset/group has no typ, return disallow
        const typeDashboardHiddenForAssets = this.optionsService.get(this.HIDE_TYPE_DASHBOARD_FOR_ASSETS, true);
        if (context.context === ViewContext.Group &&
            (typeDashboardHiddenForAssets || !context.contextData.type)) {
            return 'disallow';
        }
        // if user has no permission to change dashboard, return disallow
        if (!this.permissions.hasAnyRole(this.INVENTORY_ROLES)) {
            return 'disallow';
        }
        // case from sensor app is checked- dashboard created with sensor app has deviceType set to true but
        // type fragment is missing- we do not support this combination.
        const typeFragment = this.createFragmentKey(ContextDashboardType.Type, context?.contextData?.type);
        if (mo?.c8y_Dashboard &&
            mo?.c8y_Dashboard.deviceType &&
            context?.contextData?.type &&
            !mo[typeFragment]) {
            return 'disallow';
        }
        // if view context is Device and contextData of this device has no type yet but type dashboard can be set when type is filled,
        // return allow_if_type_filled
        if (!context?.contextData?.type &&
            context.context === ViewContext.Device &&
            this.permissions.hasAnyRole(this.INVENTORY_ROLES)) {
            return 'allow_if_type_filled';
        }
        return 'allow';
    }
    createReport(reportCfg) {
        const report = {};
        Object.assign(report, reportCfg);
        Object.assign(report, { c8y_Report: {} });
        return this.inventory.create(report);
    }
    addReportNavigatorNode(report) {
        const node = new NavigatorNode({
            label: report.name,
            path: `reports/${report.id}`,
            icon: report.icon,
            priority: report.priority
        });
        this.navigator.add(node);
    }
    getContextForGS(mo) {
        if (this.groupService.isDevice(mo)) {
            return PRODUCT_EXPERIENCE.DASHBOARD.CONTEXT.DEVICE;
        }
        else if (this.groupService.isAsset(mo)) {
            return PRODUCT_EXPERIENCE.DASHBOARD.CONTEXT.ASSET;
        }
        else if (this.groupService.isGroup(mo)) {
            return PRODUCT_EXPERIENCE.DASHBOARD.CONTEXT.GROUP;
        }
        else {
            return null;
        }
    }
    async getContextDashboards(mo, dashboardType) {
        const filterCriteria = dashboardType.map(t => ({
            // it's necessary to wrap fragment in quotes because dashboard type can contain spaces
            __has: `'${this.createDashboardFragment(mo, t)}'`
        }));
        // the has query above does not work for device type dashboards where the type contains a dot
        const typeFilterCriteria = dashboardType.includes(ContextDashboardType.Type) && mo.type
            ? {
                __and: [
                    { 'c8y_Dashboard.deviceType': { __eq: true } },
                    { 'c8y_Dashboard.deviceTypeValue': { __eq: mo.type } }
                ]
            }
            : undefined;
        const finalFilterCriteria = typeFilterCriteria
            ? [...filterCriteria, typeFilterCriteria]
            : filterCriteria;
        const query = this.queriesUtil.buildQuery({ __filter: { __or: finalFilterCriteria } });
        const now = Date.now();
        const cacheHasValidResponse = this.contextDashboardsCache &&
            this.contextDashboardsCache.query === query &&
            now - this.contextDashboardsCache.timestamp < this.CACHE_TIMEOUT;
        if (cacheHasValidResponse) {
            return this.contextDashboardsCache.result;
        }
        else {
            this.contextDashboardsCache = null;
        }
        this.contextDashboardsCache = {
            query,
            result: this.inventory.list({ query, pageSize: this.DEFAULT_PAGESIZE }),
            timestamp: now
        };
        return this.contextDashboardsCache.result;
    }
    /**
     * Creates a tuple describing the dashboard type and its fragments. For assets like devices and groups, it's possible
     * to have two fragments: one indicating this particular device/asset with its ID, and the second indicating
     * the device/asset type (if the dashboard is meant to be applied to all assets of this type).
     *
     * @param dashboardMO - Dashboard managed object.
     * @param context - Context data of asset.
     * @param name - Name of the dashboard.
     * @param isEdit - True if existing dashboard is updated, false when it's creation of new dashboard.
     * @returns Tuple of dashboard type and object containing dashboard fragments.
     */
    getDashboardFragments(dashboardMO, context, name, isEdit) {
        let dashboardType;
        const id = context?.contextData?.id || '';
        const fragments = {};
        if (name) {
            // a named dashboard should not receive any other fragments
            dashboardType = ContextDashboardType.Named;
            const namedFragmentKey = this.createFragmentKey(ContextDashboardType.Named, name);
            fragments[namedFragmentKey] = {};
        }
        else if (context?.context === ViewContext.Device || context?.context === ViewContext.Group) {
            // get base type for device or group
            const defaultType = context.context === ViewContext.Device
                ? ContextDashboardType.Device
                : ContextDashboardType.Group;
            dashboardType = dashboardMO.c8y_Dashboard.deviceType
                ? ContextDashboardType.Type
                : defaultType;
            // clear fragments from other asset if current asset is not origin of this dashboard
            this.clearRedundantFragment(dashboardMO, defaultType, fragments);
            // add base fragment for particular asset
            const deviceOrGroupFragmentKey = this.createFragmentKey(defaultType, id);
            fragments[deviceOrGroupFragmentKey] = {};
            // add or clear type fragment
            if (dashboardMO.c8y_Dashboard.deviceType || isEdit) {
                const typeFragmentKey = this.createFragmentKey(ContextDashboardType.Type, dashboardMO.c8y_Dashboard.deviceTypeValue);
                fragments[typeFragmentKey] = dashboardMO.c8y_Dashboard.deviceType ? {} : null;
            }
        }
        return [dashboardType, fragments];
    }
    /**
     * Clears fragments that originates from other managed object.
     * E.g. typed dashboard is created for device A of type c8y_MQTTDevice and id 1, so it gets fragments object
     * ```ts
     * {
     * c8y_Dashboard!device!1: {},
     * c8y_Dashboard!type!c8y_MQTTDevice: {}
     * }
     *```
     * then, on device B of type c8y_MQTTDevice and id 2, which also has access to this dashboard, deviceType is set to
     * false, so dashboard is not typed dashboard anymore and now belongs to device B, therefore fragments should look like
     * ```ts
     * {
     * c8y_Dashboard!device!1: null, // this value is cleared because dashboard is doesn't belong to device A anymore
     * c8y_Dashboard!device!2: {}, // assign dashboard to device B
     * c8y_Dashboard!type!c8y_MQTTDevice: null // this value is cleared in getDashboardFragments method as it's not typed dashboard anymore
     * }
     * ```
     *
     * @param dashboardMO - Dashboard managed object.
     * @param type - Context dashboard type.
     * @param fragments - Fragments object.
     */
    clearRedundantFragment(dashboardMO, type, fragments) {
        Object.keys(dashboardMO)
            .filter(key => key.startsWith(this.createFragmentKey(type, '')))
            .forEach(key => (fragments[key] = null));
    }
    adjustDashboardFor24Columns(dashboards) {
        if (Array.isArray(dashboards)) {
            return dashboards.map(dashboard => this.adjustDashboardFor24Columns(dashboard));
        }
        // if `columns` attribute exists, dashboard was already adjusted.
        if (dashboards.c8y_Dashboard.columns) {
            return dashboards;
        }
        dashboards.c8y_Dashboard.columns = 24;
        if (!dashboards.c8y_Dashboard.children) {
            return dashboards;
        }
        // Newly created NamedContextDashboards are still created with 12 columns for backwards compatibility.
        // Default widgets might be already configured for 24 columns.
        // If a widget is already configured for more than 12 columns, we should not adjust it.
        const alreadyHasWidgetsConfiguredForMoreThan12Columns = Object.values(dashboards.c8y_Dashboard.children).some(widget => widget._x + widget._width > 12);
        if (alreadyHasWidgetsConfiguredForMoreThan12Columns) {
            return dashboards;
        }
        // we need to multiply both _width and _x attributes with 2 to migrate from 12 to 24 columns.
        Object.values(dashboards.c8y_Dashboard.children).forEach(widget => {
            if (widget._width) {
                widget._width = widget._width * 2;
            }
            if (widget._x) {
                widget._x = widget._x * 2;
            }
        });
        return dashboards;
    }
    async serializeWidgetConfigs(dashboard) {
        const children = cloneDeep(dashboard.c8y_Dashboard.children);
        if (!children) {
            return;
        }
        const configs = Object.values(children);
        const details = configs.map(({ componentId, config }) => ({ componentId, config }));
        const results = await this.dynamicComponent.serializeConfigs(details);
        results.forEach((result, index) => {
            Object.entries(result).forEach(([key, value]) => {
                set(details[index].config, key, value);
            });
        });
        dashboard.c8y_Dashboard.children = children;
    }
    createContextDashboardCopy(dash, newContext, oldContext) {
        const children = reduce(dash.children, (_children, child) => {
            const { id } = child;
            const cfg = child.config;
            const propertiesToCopy = {
                device: device => this.replaceContextInObj(device, newContext, oldContext),
                datapoints: dataPoints => this.replaceContextInDataPoints(dataPoints, newContext, oldContext),
                dataPoints: dataPoints => this.replaceContextInDataPoints(dataPoints, newContext, oldContext),
                datapointsGauge: dataPoints => this.replaceContextInDataPoints(dataPoints, newContext, oldContext),
                datapointsLabels: dataPoints => this.replaceContextInDataPoints(dataPoints, newContext, oldContext)
            };
            if (cfg) {
                this.copyProperties(cfg, propertiesToCopy);
                if (cfg.options) {
                    this.copyProperties(cfg.options, propertiesToCopy);
                }
            }
            _children[id] = cloneDeep(child);
            return _children;
        }, {});
        dash.children = children;
        const isTypeDashboard = dash.deviceType && !!dash.deviceTypeValue;
        if (isTypeDashboard) {
            dash.deviceTypeValue = newContext.type;
        }
        return dash;
    }
    copyProperties(obj, propertiesToCopy) {
        forEach(propertiesToCopy, (copyFn, property) => {
            if (obj[property]) {
                obj[property] = copyFn(obj[property]);
            }
        });
    }
    replaceContextInDataPoints(dataPoints, newContext, oldContext) {
        dataPoints.forEach(dp => {
            dp.__target = this.replaceContextInObj(dp.__target, newContext, oldContext);
        });
        return dataPoints;
    }
    replaceContextInObj(obj, newContext, oldContext) {
        if (obj && obj.id === oldContext.id) {
            Object.assign(obj, pick(newContext, ['id', 'name']));
        }
        return obj;
    }
    getTabs$(mo, dashboardType, context) {
        const dashboards = this.getContextDashboards(mo, dashboardType);
        return this.getContextDashboards$(dashboards).pipe(mergeMap(dashboard => this.verifyDashboardAvailability$(dashboard)), 
        // Due to MTM-62321 named context dashboards included fragments for device and groups.
        // therefore some device-info dashboards (from DM app) might occur in cockpit on device level.
        // We need to filter those out..
        filter(([dashboard]) => {
            // list all identifiers of the dashboard (name, device, group, type)
            const dashboardIdentifiers = Object.keys(dashboard)
                .filter(key => key.startsWith(`${this.FRAGMENT_NAME}${this.INDEX_SPLIT}`))
                .map(key => key.split(this.INDEX_SPLIT)[1]);
            // if dashboard is not named, it's safe to include it
            if (!dashboardIdentifiers.includes(ContextDashboardType.Named)) {
                return true;
            }
            // if dashboard is named, but also has a context identifier, we skip it
            if (dashboardIdentifiers.includes(ContextDashboardType.Device) ||
                dashboardIdentifiers.includes(ContextDashboardType.Group)) {
                return false;
            }
            // all others are good..
            return true;
        }), mergeMap(([dashboard]) => this.removeDashboardMoProperty(dashboard)), tap(dashboard => this.cacheDashboard(dashboard)), map(dashboard => this.createDashboardTab(dashboard, context)), toArray());
    }
    verifyDashboardAvailability$(dashboard) {
        const globalRolesIds = dashboard?.c8y_Dashboard?.globalRolesIds;
        const canEdit = from(this.permissions.canEdit(this.INVENTORY_ROLES, dashboard, { skipRequestCheck: true }));
        const hasAnyGlobalRole = !globalRolesIds || globalRolesIds === ALL_GLOBAL_ROLES_SELECTED
            ? of(true)
            : of(this.permissions.hasAnyGlobalRole(globalRolesIds));
        return combineLatest([of(dashboard), canEdit, hasAnyGlobalRole]).pipe(filter(([, canEdit, hasAnyGlobalRole]) => canEdit || hasAnyGlobalRole));
    }
    getContextDashboards$(request) {
        return from(request).pipe(mergeMap(response => this.adjustDashboardFor24Columns(response.data)));
    }
    /**
     * Cleans already corrupted dashboards from dashboardMo property.
     * Added to fix dashboards on the cloud instance (eu-latest).
     * @deprecated This is going to be removed after 1007.7.0.
     */
    async removeDashboardMoProperty(dashboard) {
        const dashboardCopy = cloneDeep(dashboard);
        const children = get(dashboardCopy, 'c8y_Dashboard.children');
        let updateDashboard = false;
        forEach(children, child => {
            if (get(child, 'componentTransformConfigWithContext')) {
                delete child.componentTransformConfigWithContext;
                updateDashboard = true;
            }
            if (get(child, 'config.dashboardMo')) {
                delete child.config.dashboardMo;
                updateDashboard = true;
            }
        });
        if (updateDashboard) {
            await this.update(dashboardCopy);
        }
        return dashboardCopy;
    }
    cacheDashboard(dashboard) {
        this.cache.set(dashboard.id, dashboard);
    }
    createDashboardTab(dashboard, context) {
        const { c8y_Dashboard: _dashboard, id } = dashboard;
        return {
            icon: _dashboard.icon,
            path: `${this.DASHBOARD_ROUTE_PATH}/${id}`,
            label: _dashboard.name,
            priority: _dashboard.priority,
            hide: this.isReport(dashboard),
            badge: _dashboard.deviceType && this.shouldAllowToSetDashboardType(dashboard, context)
                ? gettext('Dashboard template')
                : null,
            tooltipText: _dashboard.description || gettext('Dashboard template')
        };
    }
    clean(dashboard) {
        const jsonString = JSON.stringify(dashboard, (key, value) => {
            if (key === '$$hashKey' || key === 'klasses') {
                return undefined;
            }
            return value;
        });
        return JSON.parse(jsonString);
    }
    getNamedDashboard(name) {
        return this.inventory.list({
            fragmentType: `${this.FRAGMENT_NAME}${this.INDEX_SPLIT}${ContextDashboardType.Named}${this.INDEX_SPLIT}${name}`,
            pageSize: 1
        });
    }
    createDashboardFragment(mo, type) {
        let value;
        if (mo.c8y_Report) {
            value = `${this.REPORT_PARTIAL_NAME}${mo.id}`;
        }
        else {
            value = type === ContextDashboardType.Type ? mo.type : mo.id;
        }
        return `${this.FRAGMENT_NAME}${this.INDEX_SPLIT}${type}${this.INDEX_SPLIT}${value}`;
    }
    shouldSetGlobal(dashboard, context) {
        if ((!context && this.isNamed(dashboard) && !this.isReport(dashboard)) ||
            this.isDeviceType(dashboard)) {
            return {};
        }
        return null;
    }
    getDefaultDashboard(dashboardCfg) {
        return {
            c8y_Dashboard: {
                ...dashboardCfg,
                name: dashboardCfg.name || 'Default Dashboard',
                description: gettext('This is a default dashboard.')
            },
            [`c8y_Dashboard!name!${dashboardCfg.name}`]: {}
        };
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ContextDashboardService, deps: [{ token: i1$1.InventoryService }, { token: i2.TabsService }, { token: i2.ModalService }, { token: i2$1.TranslateService }, { token: i1.Router }, { token: i2.NavigatorService }, { token: i2.Permissions }, { token: i2.AlertService }, { token: i2.DynamicComponentService }, { token: i2.GroupService }, { token: i2.OptionsService }], target: i0.ɵɵFactoryTarget.Injectable }); }
    static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ContextDashboardService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ContextDashboardService, decorators: [{
            type: Injectable,
            args: [{ providedIn: 'root' }]
        }], ctorParameters: () => [{ type: i1$1.InventoryService }, { type: i2.TabsService }, { type: i2.ModalService }, { type: i2$1.TranslateService }, { type: i1.Router }, { type: i2.NavigatorService }, { type: i2.Permissions }, { type: i2.AlertService }, { type: i2.DynamicComponentService }, { type: i2.GroupService }, { type: i2.OptionsService }] });

const DEFAULT_DUPLICATE_WITHOUT_TYPE = () => {
    throw new Error('duplicateWithoutTypeFn Not implemented');
};
const DEFAULT_PREVIEW_CHANGED = () => {
    throw new Error('previewChanged Not implemented');
};
const DEFAULT_REVERT_TO_DASHBOARD = () => {
    throw new Error('revertToDashboard Not implemented');
};
class DashboardDetailService {
    get generalSettingsForm() {
        return this.dashboardDetailsForm.get('generalSettingsForm');
    }
    get appearanceSettingsForm() {
        return this.dashboardDetailsForm.get('appearanceSettingsForm');
    }
    get widgetsForm() {
        return this.dashboardDetailsForm.get('widgetsForm');
    }
    constructor(fb, translateService, tabsService) {
        this.fb = fb;
        this.translateService = translateService;
        this.tabsService = tabsService;
        this.saveButtonDisabled = false;
        this.styling = {
            themeClass: 'dashboard-theme-light',
            headerClass: 'panel-title-regular'
        };
        this.duplicateWithoutTypeFn = DEFAULT_DUPLICATE_WITHOUT_TYPE;
        this.previewChangedFn = DEFAULT_PREVIEW_CHANGED;
        this.revertToDashboardFn = DEFAULT_REVERT_TO_DASHBOARD;
        this.DEFAULT_DASHBOARD_MARGIN = 12;
        this.DEFAULT_DASHBOARD_ICON = 'th';
        this.DEFAULT_DASHBOARD_PRIORITY = 5000;
        this.tabsService.items$
            .pipe(map$1(tabs => tabs.filter(tab => tab.tabsOutlet === DASHBOARD_DETAILS_TABS_OUTLET_NAME)), takeUntilDestroyed())
            .subscribe(tabs => (this.detailsTabs = tabs));
    }
    getTabs() {
        let tabs = this.detailsTabs;
        const shouldHideGeneralTab = !((!this.details?.isNamedDashboard && !this.details?.hideAvailability) ||
            this.details?.isReport);
        const shouldHideVersionHistoryTab = !this.details?.mo?.c8y_DashboardHistory;
        if (shouldHideGeneralTab) {
            tabs = tabs.filter(tab => tab.featureId !== DashboardDetailsTabId.GENERAL);
        }
        if (shouldHideVersionHistoryTab) {
            tabs = tabs.filter(tab => tab.featureId !== DashboardDetailsTabId.VERSIONHISTORY);
        }
        return tabs;
    }
    /**
     * Initializes dashboard details form.
     * @param dashboard Context dashboard object
     * @param isReport True if its report dashboard
     * @param allowTypeDashboard Allow to make dashboard type dashboard
     * @returns Dashboard details form
     */
    initForm(dashboard, isReport, allowTypeDashboard) {
        this.dashboardDetailsForm = this.createDashboardDetailsForm(dashboard, isReport, allowTypeDashboard);
    }
    reset() {
        this.dashboardDetailsForm = null;
        this.details = null;
        this.duplicateWithoutTypeFn = DEFAULT_DUPLICATE_WITHOUT_TYPE;
        this.previewChangedFn = DEFAULT_PREVIEW_CHANGED;
        this.revertToDashboardFn = DEFAULT_REVERT_TO_DASHBOARD;
    }
    /**
     * Creates dashboard details form divided into two groups- general settings and appearance settings.
     * @param dashboard Context dashboard object
     * @param isReport True if its report dashboard
     * @param allowTypeDashboard Allow to make dashboard type dashboard
     * @returns Dashboard details form
     */
    createDashboardDetailsForm(dashboard, isReport, allowTypeDashboard) {
        return this.fb.group({
            generalSettingsForm: this.createGeneralSettingsForm(dashboard, isReport, allowTypeDashboard),
            appearanceSettingsForm: this.createAppearanceSettingsForm(dashboard),
            widgetsForm: this.createWidgetsForm(dashboard)
        });
    }
    /**
     * Creates form group for general settings of dashboard.
     * @param dashboard Context dashboard object
     * @param isReport True if its report dashboard
     * @param allowTypeDashboard Allow to make dashboard type dashboard
     * @returns Dashboard general settings form
     */
    createGeneralSettingsForm(dashboard, isReport, allowTypeDashboard) {
        const globalRolesIdsValue = !dashboard?.globalRolesIds
            ? null
            : Array.isArray(dashboard.globalRolesIds)
                ? [...dashboard.globalRolesIds]
                : dashboard.globalRolesIds;
        return this.fb.group({
            icon: [dashboard?.icon || this.DEFAULT_DASHBOARD_ICON, [Validators.minLength(1)]],
            priority: [
                dashboard?.priority || this.DEFAULT_DASHBOARD_PRIORITY,
                [Validators.min(-10000), Validators.max(10000), Validators.required]
            ],
            name: [
                dashboard?.name ||
                    (isReport
                        ? this.translateService.instant(gettext('Report'))
                        : this.translateService.instant(gettext('Dashboard'))),
                [Validators.required, Validators.maxLength(512)]
            ],
            description: [dashboard?.description || null, [Validators.maxLength(1_000)]],
            globalRolesIds: this.fb.control(globalRolesIdsValue), // calling this.fb.control to cast type
            c8y_IsNavigatorNode: [!!dashboard?.c8y_IsNavigatorNode],
            ...(allowTypeDashboard !== 'disallow' && {
                deviceType: [
                    {
                        value: dashboard?.deviceType ?? false,
                        disabled: allowTypeDashboard === 'allow_if_type_filled'
                    }
                ]
            })
        });
    }
    /**
     * Creates form group for appearance settings of dashboard.
     * @param dashboard Context dashboard object
     * @returns Dashboard appearance settings form
     */
    createAppearanceSettingsForm(dashboard) {
        return this.fb.group({
            widgetMargin: [
                dashboard?.widgetMargin || this.DEFAULT_DASHBOARD_MARGIN,
                [Validators.min(0), Validators.max(50)]
            ],
            translateWidgetTitle: [!!dashboard?.translateWidgetTitle],
            classes: dashboard?.classes || { [this.styling.themeClass]: true },
            widgetClasses: dashboard?.widgetClasses || { [this.styling.headerClass]: true }
        });
    }
    createWidgetsForm(dashboard) {
        return this.fb.group({
            children: dashboard?.children || {}
        });
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DashboardDetailService, deps: [{ token: i5.FormBuilder }, { token: i2$1.TranslateService }, { token: i2.TabsService }], target: i0.ɵɵFactoryTarget.Injectable }); }
    static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DashboardDetailService }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DashboardDetailService, decorators: [{
            type: Injectable
        }], ctorParameters: () => [{ type: i5.FormBuilder }, { type: i2$1.TranslateService }, { type: i2.TabsService }] });

class DashboardDetailComponent {
    constructor(contextDashboardService, translateService, contextRoute, activatedRoute, tabsService, router, inventory, route, dashboardDetailService, appState, groupService, modal, gainsightService) {
        this.contextDashboardService = contextDashboardService;
        this.translateService = translateService;
        this.contextRoute = contextRoute;
        this.activatedRoute = activatedRoute;
        this.tabsService = tabsService;
        this.router = router;
        this.inventory = inventory;
        this.route = route;
        this.dashboardDetailService = dashboardDetailService;
        this.appState = appState;
        this.groupService = groupService;
        this.modal = modal;
        this.gainsightService = gainsightService;
        this.isCollapsed = true;
        this.hideAvailability = false;
        this.tabs = [];
        this.dashboardSaved = new EventEmitter();
        this.previewChanged = new EventEmitter();
        this.destroy$ = new Subject();
        this.TABS_OUTLET_NAME = DASHBOARD_DETAILS_TABS_OUTLET_NAME;
    }
    async ngOnInit() {
        this.currentContext = this.contextRoute.getContextData(this.activatedRoute);
        this.deviceTypeValue = this.context?.type;
        this.displayDeviceTypeValue =
            this.context &&
                this.groupService.isGroup(this.context) &&
                !this.groupService.isAsset(this.context)
                ? this.translateService.instant(gettext('Group'))
                : this.deviceTypeValue;
        this.updateAllowTypeDashboard();
        this.initForm();
        await this.clearNamedOutletPath(); // on init this component collapsed, but outlet may occur in URL when reloading page
    }
    ngOnChanges() {
        this.updateAllowTypeDashboard();
        this.refreshDashboardDetails();
    }
    ngAfterContentInit() {
        if (!this.isEdit) {
            this.initializeConfiguration();
        }
    }
    ngOnDestroy() {
        this.destroy$.next();
        this.destroy$.complete();
        this.dashboardDetailService.reset();
    }
    async save() {
        const oldDashboard = cloneDeep(this.dashboard);
        this.dashboard = {
            ...this.dashboard,
            ...this.dashboardDetailService.dashboardDetailsForm.get('generalSettingsForm').value,
            ...this.dashboardDetailService.dashboardDetailsForm.get('appearanceSettingsForm').value,
            ...this.dashboardDetailService.dashboardDetailsForm.get('widgetsForm').value,
            c8y_IsNavigatorNode: this.dashboardDetailService.dashboardDetailsForm.get('generalSettingsForm').value.c8y_IsNavigatorNode
                ? {}
                : this.currentDashboard
                    ? null
                    : undefined
        };
        const dashboardSettingChanges = Object.keys(this.dashboard).filter(key => {
            if (key === 'c8y_IsNavigatorNode') {
                return !!oldDashboard[key] !== !!this.dashboard[key];
            }
            return !isEqual(oldDashboard[key], this.dashboard[key]);
        });
        this.dashboard.author = this.appState.currentUser.value.userName;
        this.dashboard.deviceTypeValue ??= this.deviceTypeValue;
        this.dashboard.historyDescription =
            this.updateDashboardHistoryDescription(dashboardSettingChanges);
        if (!this.isEdit && !this.isReport) {
            await this.createNewDashboard();
        }
        else if (!this.isEdit && this.isReport) {
            await this.createNewReport();
        }
        else {
            await this.updateDashboard();
            this.mo.c8y_DashboardHistory = [
                this.dashboard,
                ...(this.mo?.c8y_DashboardHistory ? this.mo.c8y_DashboardHistory : [])
            ];
        }
    }
    updateDashboardHistoryDescription(dashboardChanges) {
        return {
            changeType: this.isEdit ? 'update' : 'create',
            dashboardSettingChanges: dashboardChanges
                .filter(value => value !== 'deviceType')
                .map(value => DASHBOARD_SETTINGS_CHANGES[value] || value),
            deviceType: dashboardChanges.find(value => value === 'deviceType')
                ? this.dashboard.deviceType
                : undefined
        };
    }
    async cancel() {
        if (!this.isEdit && !this.isReport) {
            // dashboard creation in it's own tab- confirm and cleanup triggered in canDeactivate during routing
            this.tabsService.items$.pipe(first()).subscribe((tabs) => {
                const tab = tabs.find(t => t.featureId !== newDashboardTab.featureId);
                this.router.navigate(typeof tab.path === 'string' ? [tab.path] : tab.path, {
                    replaceUrl: true
                });
            });
            return;
        }
        else if (!this.isEdit && this.isReport) {
            this.router.navigate(['reports']);
            return;
        }
        else if (this.dashboardDetailService.dashboardDetailsForm.dirty &&
            !(await this.confirmClosing())) {
            return;
        }
        await this.clearNamedOutletPath();
        this.cleanupAfterDetailsClose();
    }
    async canDeactivate(omitConfirm = false) {
        let canDeactivate;
        if (this.isCollapsed || !this.dashboardDetailService.dashboardDetailsForm.dirty) {
            canDeactivate = true;
        }
        else {
            if (omitConfirm) {
                canDeactivate = false;
            }
            else {
                canDeactivate = await this.confirmClosing();
            }
        }
        if (canDeactivate) {
            this.cleanupAfterDetailsClose();
        }
        return canDeactivate;
    }
    show(isEdit) {
        this.isEdit = isEdit;
        if (this.isEdit) {
            this.edit();
        }
        this.initForm();
        this.isCollapsed = false;
        this.refreshDashboardDetails();
    }
    async revertDashboard(dashboard) {
        this.dashboard = dashboard;
        this.dashboard.c8y_IsNavigatorNode = this.dashboard.c8y_IsNavigatorNode
            ? {}
            : this.currentDashboard
                ? null
                : undefined;
        await this.updateDashboard();
    }
    async duplicateWithoutType() {
        const newDashboardName = this.translateService.instant(gettext('{{ dashboardName }} (copy)'), {
            dashboardName: this.dashboardDetailService.dashboardDetailsForm.get('generalSettingsForm').value.name
        });
        this.dashboardDetailService.dashboardDetailsForm.get('generalSettingsForm').patchValue({
            ...this.dashboardDetailService.dashboardDetailsForm.get('generalSettingsForm').value,
            deviceType: false,
            name: newDashboardName
        });
        // setting edition to false so new dashboard can be created as copy of current one
        this.isEdit = false;
        await this.save();
    }
    updateAllowTypeDashboard() {
        this.allowTypeDashboard = this.contextDashboardService.shouldAllowToSetDashboardType(this.mo, this.currentContext);
    }
    initForm() {
        this.dashboardDetailService.initForm(this.dashboard, this.isReport, this.allowTypeDashboard);
        this.dashboardDetailsForm = this.dashboardDetailService.dashboardDetailsForm;
    }
    async confirmClosing() {
        try {
            await this.modal.confirm(gettext('Close dashboard details'), gettext('Are you sure you want to close dashboard details? All unsaved changes will be lost.'), Status.WARNING, {
                ok: gettext('Close'),
                cancel: gettext('Cancel')
            });
            return true;
        }
        catch (e) {
            return false;
        }
    }
    async createNewDashboard() {
        const mo = await this.contextDashboardService.create(this.dashboard, this.currentContext, this.isNamedDashboard ? this.dashboard.name : undefined);
        await this.onCreateGSEvent(mo);
        await this.clearNamedOutletPath();
        this.cleanupAfterDetailsClose();
        await this.contextDashboardService.navigateToDashboard(mo, true);
    }
    async onCreateGSEvent(mo) {
        const parentName = await this.convertStringToHash(this.context?.name);
        const dashboardName = await this.convertStringToHash(this.dashboard?.name);
        this.gainsightService.triggerEvent(PRODUCT_EXPERIENCE.DASHBOARD.EVENTS.DASHBOARDS, {
            component: PRODUCT_EXPERIENCE.DASHBOARD.COMPONENTS.ADD_DASHBOARD,
            action: PRODUCT_EXPERIENCE.DASHBOARD.ACTIONS.CREATE,
            name: dashboardName,
            id: mo.id,
            nameId: `${dashboardName}_${this.mo.id}`,
            isDashboardTemplate: mo.c8y_Dashboard.deviceType,
            parentAssetId: this.context?.id ? this.context.id : 'noContext',
            parentAssetName: this.context?.id ? parentName : 'noContext',
            parentAssetType: this.context?.id ? this.context.type : 'noContext',
            parentAssetNameId: this.context?.id ? `${parentName}_${this.context.id}` : 'noContext',
            parentAssetNameDashboardName: this.context?.id
                ? `${parentName}_${dashboardName}`
                : 'noContext',
            parentAssetIdDashboardId: this.context?.id ? `${this.context.id}_${this.mo.id}` : 'noContext',
            parentAssetNameDashboardId: this.context?.id ? `${parentName}_${this.mo.id}` : 'noContext',
            parentAssetNameIdDashboardNameId: this.context?.id
                ? `${parentName}_${this.context.id}_${dashboardName}_${this.mo.id}`
                : 'noContext',
            dashboardType: mo.c8y_Dashboard.deviceType ? mo.c8y_Dashboard.deviceTypeValue : null,
            context: this.contextDashboardService.getContextForGS(this.currentContext?.contextData)
        });
    }
    async convertStringToHash(str) {
        if (!str) {
            return null;
        }
        return (await this.gainsightService.shouldSendPiiData())
            ? str
            : await this.gainsightService.hashGroupName(str);
    }
    async createNewReport() {
        try {
            const { name, icon, c8y_IsNavigatorNode, priority, description } = this.dashboard;
            const report = (await this.contextDashboardService.createReport({
                name,
                icon,
                c8y_IsNavigatorNode,
                priority,
                description
            })).data;
            await this.contextDashboardService.create(this.dashboard, undefined, `${this.contextDashboardService.REPORT_PARTIAL_NAME}${report.id}`);
            this.gainsightService.triggerEvent(PRODUCT_EXPERIENCE.DASHBOARD.EVENTS.REPORTS, {
                component: PRODUCT_EXPERIENCE.DASHBOARD.COMPONENTS.ADD_REPORT,
                action: PRODUCT_EXPERIENCE.DASHBOARD.ACTIONS.CREATE,
                name: report.name,
                id: report.id,
                context: PRODUCT_EXPERIENCE.DASHBOARD.CONTEXT.REPORT
            });
            if (report.c8y_IsNavigatorNode) {
                this.contextDashboardService.addReportNavigatorNode(report);
            }
            await this.clearNamedOutletPath();
            this.cleanupAfterDetailsClose();
            await this.router.navigate(['reports', report.id]);
        }
        catch (ex) {
            // intended empty
        }
    }
    async updateDashboard() {
        try {
            const dashboardMO = cloneDeep(this.mo);
            const cfg = this.dashboard;
            if (this.isReport) {
                const { name, icon, c8y_IsNavigatorNode, priority, description } = cfg;
                dashboardMO.c8y_Dashboard = cfg;
                await this.updateReport({
                    id: this.context.id,
                    name,
                    icon,
                    c8y_IsNavigatorNode,
                    priority,
                    description
                });
            }
            else {
                dashboardMO.c8y_Dashboard = cfg;
            }
            const updatedDashboardMo = await this.contextDashboardService.update(dashboardMO, this.currentContext);
            await this.clearNamedOutletPath();
            this.cleanupAfterDetailsClose();
            this.dashboardSaved.emit(updatedDashboardMo);
        }
        catch (ex) {
            // intended empty
        }
    }
    edit() {
        if (this.isReport) {
            const { name, icon, priority, c8y_IsNavigatorNode, description } = this.context;
            Object.assign(this.dashboard, { name, icon, priority, c8y_IsNavigatorNode, description });
        }
        this.initializeConfiguration();
    }
    initializeConfiguration() {
        const defaultDashboardCfg = {
            name: this.translateService.instant(this.isReport ? gettext('Report') : gettext('Dashboard')),
            priority: this.isReport
                ? REPORT_DEFAULT_NAVIGATION_NODE_PRIORITY
                : this.dashboardDetailService.DEFAULT_DASHBOARD_PRIORITY,
            icon: this.dashboardDetailService.DEFAULT_DASHBOARD_ICON,
            deviceTypeValue: this.deviceType,
            columns: 24
        };
        if (this.dashboard) {
            this.currentDashboard = clone(this.dashboard);
        }
        else {
            this.dashboard = defaultDashboardCfg;
            this.dashboardDetailService.dashboardDetailsForm.markAsDirty();
        }
    }
    async updateReport(mo) {
        mo.c8y_IsNavigatorNode = mo.c8y_IsNavigatorNode ? {} : null;
        const res = await this.inventory.update(mo);
        this.context = res.data;
        if (this.route.parent) {
            this.route.parent.snapshot.data.contextData = this.context;
        }
        this.contextDashboardService.updateNavigatorItem(res.data);
    }
    cleanupAfterDetailsClose() {
        this.isCollapsed = true;
        if (!this.isEdit) {
            const dashboardContext = this.route.snapshot.data.dashboard;
            // hide New dashboard tab
            const tmpNewDashboardTab = [...this.tabsService.state].find(t => t.featureId === newDashboardTab.featureId);
            // don't hide the new dashboard tab when clicking on the add dashboard button from another dashboard.
            if (!tmpNewDashboardTab || dashboardContext?.c8y_Dashboard) {
                return;
            }
            // hide tab and make it appear as last one to prevent navigating to it
            tmpNewDashboardTab.hide = true;
            tmpNewDashboardTab.priority = -Infinity;
            this.tabsService.refresh();
        }
        this.previewChanged.emit(this.dashboard);
    }
    async clearNamedOutletPath() {
        if (this.activatedRoute.snapshot?.children?.[0]?.routeConfig.outlet === DASHBOARD_DETAILS_OUTLET) {
            await this.router.navigate([
                {
                    outlets: {
                        [DASHBOARD_DETAILS_OUTLET]: null
                    }
                }
            ], { queryParamsHandling: 'preserve', relativeTo: this.activatedRoute });
        }
    }
    refreshDashboardDetails() {
        this.dashboardDetailService.details = {
            isReport: this.isReport,
            isNamedDashboard: this.isNamedDashboard,
            hideAvailability: this.hideAvailability,
            dashboard: this.dashboard,
            deviceTypeValue: this.deviceType,
            displayDeviceTypeValue: this.displayDeviceTypeValue,
            mo: this.mo,
            allowTypeDashboard: this.allowTypeDashboard,
            isDevice: !!this.context?.c8y_IsDevice,
            context: this.context
        };
        this.dashboardDetailService.duplicateWithoutTypeFn = () => this.duplicateWithoutType();
        this.dashboardDetailService.previewChangedFn = dashboardClasses => this.previewChanged.emit(dashboardClasses);
        this.dashboardDetailService.revertToDashboardFn = dashboard => this.revertDashboard(dashboard);
        this.tabs = this.dashboardDetailService.getTabs();
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DashboardDetailComponent, deps: [{ token: ContextDashboardService }, { token: i2$1.TranslateService }, { token: i2.ContextRouteService }, { token: i1.ActivatedRoute }, { token: i2.TabsService }, { token: i1.Router }, { token: i1$1.InventoryService }, { token: i1.ActivatedRoute }, { token: DashboardDetailService }, { token: i2.AppStateService }, { token: i2.GroupService }, { token: i2.ModalService }, { token: i2.GainsightService }], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.14", type: DashboardDetailComponent, isStandalone: true, selector: "c8y-dashboard-detail", inputs: { isReport: "isReport", deviceType: "deviceType", context: "context", mo: "mo", dashboard: "dashboard", isNamedDashboard: "isNamedDashboard", hideAvailability: "hideAvailability" }, outputs: { dashboardSaved: "dashboardSaved", previewChanged: "previewChanged" }, providers: [DashboardDetailService], usesOnChanges: true, ngImport: i0, template: "<div\n  class=\"collapse c8y-top-drawer\"\n  [collapse]=\"isCollapsed\"\n  [isAnimated]=\"true\"\n>\n  <div class=\"p-t-16 p-b-16 p-l-24 p-r-24 separator-bottom\">\n    <span class=\"h4\">{{ 'Dashboard settings' | translate }}</span>\n  </div>\n\n  <div\n    class=\"d-flex\"\n    *ngIf=\"!isCollapsed\"\n  >\n    <c8y-tabs-outlet\n      class=\"c8y-top-drawer--tabs\"\n      [tabs]=\"tabs\"\n      [outletName]=\"TABS_OUTLET_NAME\"\n      [orientation]=\"'vertical'\"\n      [openFirstTab]=\"true\"\n    ></c8y-tabs-outlet>\n\n    <div\n      class=\"inner-scroll c8y-top-drawer__tabs-container flex-grow p-l-24 p-r-lg-32 p-r-xs-24 p-r-sm-24\"\n    >\n      <router-outlet name=\"dashboard-details\"></router-outlet>\n    </div>\n  </div>\n\n  <div class=\"c8y-top-drawer--footer\">\n    <button\n      class=\"btn btn-default\"\n      title=\"{{ 'Cancel' | translate }}\"\n      type=\"button\"\n      data-cy=\"dashboard-detail--cancel-dashboard\"\n      (click)=\"cancel()\"\n    >\n      {{ 'Cancel' | translate }}\n    </button>\n    <button\n      class=\"btn btn-primary\"\n      title=\"{{ 'Save' | translate }}\"\n      type=\"button\"\n      data-cy=\"dashboard-detail--save-dashboard\"\n      (click)=\"save()\"\n      [disabled]=\"((dashboardDetailsForm?.invalid || dashboardDetailsForm?.pristine) && this.isEdit) || dashboardDetailService.saveButtonDisabled\"\n    >\n      {{ 'Save' | translate }}\n    </button>\n  </div>\n</div>\n", dependencies: [{ kind: "directive", type: CollapseDirective, selector: "[collapse]", inputs: ["display", "isAnimated", "collapse"], outputs: ["collapsed", "collapses", "expanded", "expands"], exportAs: ["bs-collapse"] }, { kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: TabsOutletComponent, selector: "c8y-tabs-outlet,c8y-ui-tabs", inputs: ["tabs", "orientation", "navigatorOpen", "outletName", "context", "openFirstTab", "hasHeader"] }, { kind: "directive", type: RouterOutlet, selector: "router-outlet", inputs: ["name", "routerOutletData"], outputs: ["activate", "deactivate", "attach", "detach"], exportAs: ["outlet"] }, { kind: "pipe", type: C8yTranslatePipe, name: "translate" }] }); }
}
__decorate([
    memoize(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [String]),
    __metadata("design:returntype", Promise)
], DashboardDetailComponent.prototype, "convertStringToHash", null);
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DashboardDetailComponent, decorators: [{
            type: Component,
            args: [{ selector: 'c8y-dashboard-detail', providers: [DashboardDetailService], imports: [CollapseDirective, NgIf, TabsOutletComponent, RouterOutlet, C8yTranslatePipe], template: "<div\n  class=\"collapse c8y-top-drawer\"\n  [collapse]=\"isCollapsed\"\n  [isAnimated]=\"true\"\n>\n  <div class=\"p-t-16 p-b-16 p-l-24 p-r-24 separator-bottom\">\n    <span class=\"h4\">{{ 'Dashboard settings' | translate }}</span>\n  </div>\n\n  <div\n    class=\"d-flex\"\n    *ngIf=\"!isCollapsed\"\n  >\n    <c8y-tabs-outlet\n      class=\"c8y-top-drawer--tabs\"\n      [tabs]=\"tabs\"\n      [outletName]=\"TABS_OUTLET_NAME\"\n      [orientation]=\"'vertical'\"\n      [openFirstTab]=\"true\"\n    ></c8y-tabs-outlet>\n\n    <div\n      class=\"inner-scroll c8y-top-drawer__tabs-container flex-grow p-l-24 p-r-lg-32 p-r-xs-24 p-r-sm-24\"\n    >\n      <router-outlet name=\"dashboard-details\"></router-outlet>\n    </div>\n  </div>\n\n  <div class=\"c8y-top-drawer--footer\">\n    <button\n      class=\"btn btn-default\"\n      title=\"{{ 'Cancel' | translate }}\"\n      type=\"button\"\n      data-cy=\"dashboard-detail--cancel-dashboard\"\n      (click)=\"cancel()\"\n    >\n      {{ 'Cancel' | translate }}\n    </button>\n    <button\n      class=\"btn btn-primary\"\n      title=\"{{ 'Save' | translate }}\"\n      type=\"button\"\n      data-cy=\"dashboard-detail--save-dashboard\"\n      (click)=\"save()\"\n      [disabled]=\"((dashboardDetailsForm?.invalid || dashboardDetailsForm?.pristine) && this.isEdit) || dashboardDetailService.saveButtonDisabled\"\n    >\n      {{ 'Save' | translate }}\n    </button>\n  </div>\n</div>\n" }]
        }], ctorParameters: () => [{ type: ContextDashboardService }, { type: i2$1.TranslateService }, { type: i2.ContextRouteService }, { type: i1.ActivatedRoute }, { type: i2.TabsService }, { type: i1.Router }, { type: i1$1.InventoryService }, { type: i1.ActivatedRoute }, { type: DashboardDetailService }, { type: i2.AppStateService }, { type: i2.GroupService }, { type: i2.ModalService }, { type: i2.GainsightService }], propDecorators: { isReport: [{
                type: Input
            }], deviceType: [{
                type: Input
            }], context: [{
                type: Input
            }], mo: [{
                type: Input
            }], dashboard: [{
                type: Input
            }], isNamedDashboard: [{
                type: Input
            }], hideAvailability: [{
                type: Input
            }], dashboardSaved: [{
                type: Output
            }], previewChanged: [{
                type: Output
            }], convertStringToHash: [] } });

/**
 * Holds current state of dashboard children during edit mode.
 */
class DashboardOriginatorService {
    /**
     * Current dashboard children state name for indicating what change will be undone.
     */
    get currentStateName() {
        return this.state?.name;
    }
    /**
     * Initializes dashboard children state on edit mode start.
     * @param state Dashboard children initial state.
     */
    initState(state) {
        this.state = state;
    }
    /**
     * Updates dashboard children state when it is changed or restored with undo/redo.
     * @param state Dashboard children updated state.
     */
    setState(state) {
        this.state = state;
    }
    /**
     * Returns copy of current state.
     */
    getState() {
        return cloneDeep(this.state);
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DashboardOriginatorService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
    static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DashboardOriginatorService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DashboardOriginatorService, decorators: [{
            type: Injectable,
            args: [{
                    providedIn: 'root'
                }]
        }] });

/**
 * Caretaker is responsible for undo and redo functionalities.
 */
class DashboardCaretakerService {
    /**
     * Current undo stack length for defining undo button disable state.
     */
    get undoStackLength() {
        return this.undoStack.length;
    }
    /**
     * Redo stack latest item for defining redo button disable state and its' title.
     */
    get redoStackLastItem() {
        return this.redoStack[this.redoStack.length - 1];
    }
    constructor(originator) {
        this.originator = originator;
        /**
         * List of dashboard children changes.
         * @private
         */
        this.undoStack = [];
        /**
         * List of dashboard children changes that were undone and can be redone back.
         * @private
         */
        this.redoStack = [];
    }
    /**
     * Adds current dashboard children state to undo stack and clears redo stack.
     */
    backup() {
        this.undoStack.push(this.originator.getState());
        this.redoStack = [];
    }
    /**
     * Reverts last dashboard children change and puts it in redo stack.
     * @returns Current state of dashboard children after reverting last change.
     */
    undo() {
        if (!this.undoStack.length) {
            return;
        }
        const memento = this.undoStack.pop();
        this.redoStack.push(this.originator.getState());
        this.originator.setState(memento);
        return memento;
    }
    /**
     * Applies previously undone change.
     * @returns Current state of dashboard children after applying previously undone change.
     */
    redo() {
        if (!this.redoStack.length) {
            return;
        }
        this.undoStack.push(this.originator.getState());
        const memento = this.redoStack.pop();
        this.originator.setState(memento);
        return memento;
    }
    /**
     * Clears both undo and redo stack, sets current state as initial state and returns initial state.
     * @returns Initial state of dashboard children.
     */
    reset() {
        const initialItem = this.undoStack[0] || this.originator.getState();
        this.undoStack = [];
        this.redoStack = [];
        this.originator.initState(initialItem);
        return initialItem;
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DashboardCaretakerService, deps: [{ token: DashboardOriginatorService }], target: i0.ɵɵFactoryTarget.Injectable }); }
    static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DashboardCaretakerService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DashboardCaretakerService, decorators: [{
            type: Injectable,
            args: [{
                    providedIn: 'root'
                }]
        }], ctorParameters: () => [{ type: DashboardOriginatorService }] });

/**
 * Facade for implementation of Memento design pattern (caretaker, originator). Responsible for undo/redo feature
 * during edit mode of dashboard.
 */
class DashboardEditModeService {
    get undoButtonDisabled() {
        return this.caretaker.undoStackLength === 0;
    }
    /**
     * Current dashboard change name for undo button title.
     */
    get changeToUndoName() {
        return this.originator.currentStateName;
    }
    /**
     * Redo stack latest item for defining redo button disable state and its title.
     */
    get redoStackLastItem() {
        return this.caretaker.redoStackLastItem;
    }
    constructor(originator, caretaker) {
        this.originator = originator;
        this.caretaker = caretaker;
    }
    /**
     * Initializes dashboard children state on edit mode start.
     * @param state Dashboard children initial state.
     */
    init(state) {
        this.originator.initState(state);
    }
    /**
     * Saves current state for further undo actions and sets new dashboard children state.
     * @param state Dashboard children updated state.
     */
    newState(state) {
        this.caretaker.backup();
        this.originator.setState(state);
    }
    /**
     * Returns copy of current state.
     */
    getCurrentState() {
        return this.originator.getState();
    }
    /**
     * Reverts last dashboard children change.
     * @returns Current state of dashboard children after reverting last change.
     */
    undo() {
        return this.caretaker.undo();
    }
    /**
     * Applies previously undone change.
     * @returns Current state of dashboard children after applying previously undone change.
     */
    redo() {
        return this.caretaker.redo();
    }
    /**
     * Resets dashboard children state to initial one and returns initial state.
     * @returns Initial state of dashboard children.
     */
    reset() {
        return this.caretaker.reset();
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DashboardEditModeService, deps: [{ token: DashboardOriginatorService }, { token: DashboardCaretakerService }], target: i0.ɵɵFactoryTarget.Injectable }); }
    static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DashboardEditModeService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DashboardEditModeService, decorators: [{
            type: Injectable,
            args: [{
                    providedIn: 'root'
                }]
        }], ctorParameters: () => [{ type: DashboardOriginatorService }, { type: DashboardCaretakerService }] });

/**
 * A hook to add dynamic configurations to any widget configuration.
 * @deprecated Consider using the `hookWidgetConfig` function instead.
 */
const HOOK_WIDGET_CONFIG = new InjectionToken('HOOK_WIDGET_CONFIG');
/**
 * A hook to add dynamic components to the UI (e.g. widgets).
 *
 * You can either provide a single `DynamicComponentDefinition` as parameter:
 * ```typescript
 *  hookWidgetConfig(...)
 * ```
 *
 * Or an array to directly register multiple:
 * ```typescript
 *  hookWidgetConfig([...])
 * ```
 *
 * Or you provide an Service that implements `ExtensionFactory<DynamicComponentDefinition>`
 * ```typescript
 *  export class MyDynamicComponentDefinitionFactory implements ExtensionFactory<DynamicComponentDefinition> {...}
 *  ...
 *  hookWidgetConfig(MyDynamicComponentDefinitionFactory)
 * ```
 * A typed alternative to `HOOK_WIDGET_CONFIG`.
 * @param components The `DynamicComponentDefinition`'s or `ExtensionFactory` to be provided.
 * @returns An `Provider` to be provided in your module.
 */
function hookWidgetConfig(components, options) {
    return hookGeneric(components, HOOK_WIDGET_CONFIG, options);
}

class WidgetConfigSectionService extends ExtensionPointForPlugins {
    constructor(rootInjector, router, plugins) {
        super(rootInjector, plugins);
        this.router = router;
        this.plugins = plugins;
        this.DEFAULT_WAIT_TIME = 100;
        this.items$ = this.setupItemsObservable();
    }
    get state() {
        return this.state$.value;
    }
    add(component) {
        this.state.add(component);
        this.emitNewState();
    }
    remove(component) {
        this.state.delete(component);
        this.emitNewState();
    }
    setupItemsObservable() {
        const remotesLoaded$ = this.plugins.allPluginsLoaded$.pipe(filter(loaded => !!loaded), take(1));
        return fromTriggerOnce(this.router, this.refresh$, [
            getInjectedHooks(HOOK_WIDGET_CONFIG, this.injectors),
            () => this.factories,
            stateToFactory(this.state$)
        ]).pipe(distinctUntilChanged(), takeUntil(combineLatest([timer(this.DEFAULT_WAIT_TIME), remotesLoaded$])), shareReplay(1));
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: WidgetConfigSectionService, deps: [{ token: i0.Injector }, { token: i1.Router }, { token: i2.PluginsResolveService }], target: i0.ɵɵFactoryTarget.Injectable }); }
    static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: WidgetConfigSectionService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: WidgetConfigSectionService, decorators: [{
            type: Injectable,
            args: [{
                    providedIn: 'root'
                }]
        }], ctorParameters: () => [{ type: i0.Injector }, { type: i1.Router }, { type: i2.PluginsResolveService }] });

class WidgetConfigSectionComponent {
    setFeedbackTemplate(template) {
        this.feedbackTemplate = template;
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: WidgetConfigSectionComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.14", type: WidgetConfigSectionComponent, isStandalone: true, selector: "c8y-widget-config-section", inputs: { section: "section" }, ngImport: i0, template: "<c8y-li\n  class=\"c8y-list__item--no-expand c8y-list__item--sticky-top c8y-list__item--dense full-w-collapse\"\n  [collapsed]=\"!section.expanded\"\n  #li\n>\n  <div class=\"d-flex p-r-16 fit-w\">\n    <button\n      class=\"btn-clean flex-grow flex-no-shrink\"\n      type=\"button\"\n      [attr.data-cy]=\"section.label\"\n      (click)=\"li.collapsed = !li.collapsed\"\n    >\n      <i\n        class=\"p-r-4\"\n        [c8yIcon]=\"li.collapsed ? 'chevron-right' : 'chevron-down'\"\n      ></i>\n      <span\n        class=\"text-medium\"\n        translate\n      >\n        {{ section.label }}\n      </span>\n    </button>\n    <ng-container *ngTemplateOutlet=\"feedbackTemplate\"></ng-container>\n  </div>\n  <c8y-list-item-collapse>\n    <ng-container\n      *c8yComponentOutlet=\"\n        section.component;\n        initialState: section.initialState;\n        environmentInjector: section.injector\n      \"\n    ></ng-container>\n  </c8y-list-item-collapse>\n</c8y-li>\n", dependencies: [{ kind: "ngmodule", type: ListGroupModule }, { kind: "component", type: i2.ListItemComponent, selector: "c8y-list-item, c8y-li", inputs: ["active", "highlighted", "emptyActions", "dense", "collapsed", "selectable"], outputs: ["collapsedChange"] }, { kind: "component", type: i2.ListItemCollapseComponent, selector: "c8y-list-item-collapse, c8y-li-collapse", inputs: ["collapseWay"] }, { kind: "directive", type: C8yComponentOutlet, selector: "[c8yComponentOutlet]", inputs: ["c8yComponentOutlet", "c8yComponentOutletInjector", "c8yComponentOutletEnvironmentInjector", "c8yComponentOutletProviders", "c8yComponentOutletInitialState"] }, { kind: "directive", type: IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: CommonModule }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: WidgetConfigSectionComponent, decorators: [{
            type: Component,
            args: [{ selector: 'c8y-widget-config-section', standalone: true, imports: [ListGroupModule, C8yComponentOutlet, IconDirective, NgTemplateOutlet, CommonModule], template: "<c8y-li\n  class=\"c8y-list__item--no-expand c8y-list__item--sticky-top c8y-list__item--dense full-w-collapse\"\n  [collapsed]=\"!section.expanded\"\n  #li\n>\n  <div class=\"d-flex p-r-16 fit-w\">\n    <button\n      class=\"btn-clean flex-grow flex-no-shrink\"\n      type=\"button\"\n      [attr.data-cy]=\"section.label\"\n      (click)=\"li.collapsed = !li.collapsed\"\n    >\n      <i\n        class=\"p-r-4\"\n        [c8yIcon]=\"li.collapsed ? 'chevron-right' : 'chevron-down'\"\n      ></i>\n      <span\n        class=\"text-medium\"\n        translate\n      >\n        {{ section.label }}\n      </span>\n    </button>\n    <ng-container *ngTemplateOutlet=\"feedbackTemplate\"></ng-container>\n  </div>\n  <c8y-list-item-collapse>\n    <ng-container\n      *c8yComponentOutlet=\"\n        section.component;\n        initialState: section.initialState;\n        environmentInjector: section.injector\n      \"\n    ></ng-container>\n  </c8y-list-item-collapse>\n</c8y-li>\n" }]
        }], propDecorators: { section: [{
                type: Input
            }] } });

class WidgetConfigFeedbackComponent {
    constructor() {
        this.widgetSection = inject(WidgetConfigSectionComponent);
    }
    ngAfterViewInit() {
        this.widgetSection.setFeedbackTemplate(this.template);
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: WidgetConfigFeedbackComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.14", type: WidgetConfigFeedbackComponent, isStandalone: true, selector: "c8y-widget-config-feedback", viewQueries: [{ propertyName: "template", first: true, predicate: TemplateRef, descendants: true }], ngImport: i0, template: "<ng-template #tpl>\n  <ng-content></ng-content>\n</ng-template>\n" }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: WidgetConfigFeedbackComponent, decorators: [{
            type: Component,
            args: [{ selector: 'c8y-widget-config-feedback', standalone: true, imports: [WidgetConfigSectionComponent], template: "<ng-template #tpl>\n  <ng-content></ng-content>\n</ng-template>\n" }]
        }], propDecorators: { template: [{
                type: ViewChild,
                args: [TemplateRef]
            }] } });

class WidgetAssetSelectorComponent {
    constructor() {
        this.widgetConfigService = inject(WidgetConfigService);
        this.sectionComponent = inject(WidgetConfigSectionComponent);
        this.inventoryService = inject(InventoryService);
        this.alertService = inject(AlertService);
        /**
         * If set to true, a device must be selected. Default is true.
         */
        this.isRequired = true;
    }
    /**
     * @ignore
     */
    async ngOnInit() {
        if (this.widgetConfigService.currentConfig.device) {
            try {
                if (this.widgetConfigService.currentConfig.device.self) {
                    this.selectedDevice = this.widgetConfigService.currentConfig.device;
                }
                else {
                    this.selectedDevice = (await this.inventoryService.detail(this.widgetConfigService.currentConfig.device.id)).data;
                }
            }
            catch (ex) {
                // intended empty catch block to handle cases where the device might not be found
                // error messages however will be displayed in the widget config feedback component
            }
        }
        else if (this.isRequired && !this.hasContext(this.widgetConfigService.currentConfig)) {
            this.sectionComponent.section.expanded = true;
        }
        else if (this.hasContext(this.widgetConfigService.currentConfig)) {
            this.selectedDevice = this.widgetConfigService.currentConfig.settings?.context;
        }
        this.assetSelectorConfig = {
            view: 'miller',
            groupsSelectable: this.groupsSelectable || this.widgetConfigService.currentConfig.settings?.groupsSelectable,
            showChildDevices: true,
            columnHeaders: true,
            showUnassignedDevices: this.showUnassigned ?? true,
            search: !this.widgetConfigService.currentConfig.settings?.context?.additionParents,
            showFilter: true,
            showSelected: false,
            singleColumn: true,
            label: '',
            modelMode: 'full',
            preventInitialSelect: false,
            ...this.assetSelectorConfig
        };
    }
    /**
     * Returns true if a context is set.
     * @param value The current configuration of the widget.
     * @returns true if a context is set, false otherwise.
     */
    hasContext(value) {
        return value?.settings?.context && Object.keys(value.settings.context).length > 0;
    }
    /**
     * Update the configuration of the widget if the selection was changed.
     * @param $event The change event of the selection.
     */
    selectionChanged($event) {
        if ($event.change.isSelected) {
            this.updateConfig($event.change.item);
        }
    }
    updateConfig(device) {
        this.widgetConfigService.updateConfig({ device });
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: WidgetAssetSelectorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.14", type: WidgetAssetSelectorComponent, isStandalone: true, selector: "c8y-widget-asset-selector", inputs: { isRequired: "isRequired", showUnassigned: "showUnassigned", groupsSelectable: "groupsSelectable", assetSelectorConfig: "assetSelectorConfig", selectedDevice: "selectedDevice" }, host: { classAttribute: "bg-level-1" }, ngImport: i0, template: "<div class=\"card borderless\">\n  <div\n    class=\"card-block p-0 bg-inherit\"\n    style=\"height: 315px\"\n  >\n    <!-- \n      ngIf is needed to avoid a race condition as \n      asset-selector seems not to support dynamic config changes\n    -->\n    <c8y-asset-selector-miller\n      class=\"d-block bg-inherit p-relative\"\n      *ngIf=\"assetSelectorConfig\"\n      (onSelected)=\"selectionChanged($event)\"\n      [config]=\"assetSelectorConfig\"\n      [asset]=\"(widgetConfigService.currentConfig$ | async).settings?.context\"\n      [(ngModel)]=\"selectedDevice\"\n      [required]=\"isRequired\"\n      name=\"configAsset\"\n    ></c8y-asset-selector-miller>\n  </div>\n\n  <c8y-widget-config-feedback>\n    <span\n      class=\"tag chip text-12 m-4\"\n      [ngClass]=\"{\n        'tag--info': selectedDevice || !isRequired,\n        'tag--danger': !selectedDevice && isRequired\n      }\"\n    >\n      <button\n        class=\"btn-clean text-12 m-r-4\"\n        title=\"{{ 'Deselect' | translate }}\"\n        type=\"button\"\n        *ngIf=\"selectedDevice\"\n        (click)=\"selectedDevice = null; updateConfig(null)\"\n      >\n        <i c8yIcon=\"times\"></i>\n      </button>\n      <span\n        class=\"text-truncate\"\n        title=\"{{ selectedDevice.name || 'Device ' + selectedDevice.id }}\"\n        *ngIf=\"selectedDevice\"\n      >\n        {{ selectedDevice.name || 'Device ' + selectedDevice.id }}\n      </span>\n      <span\n        *ngIf=\"!selectedDevice\"\n        translate\n      >\n        No asset selected\n      </span>\n    </span>\n  </c8y-widget-config-feedback>\n</div>\n", dependencies: [{ kind: "ngmodule", type: AssetSelectorModule }, { kind: "component", type: i1$2.MillerViewComponent, selector: "c8y-asset-selector-miller", inputs: ["config", "asset", "selectedDevice", "rootNode", "container"], outputs: ["onSelected", "onClearSelected"] }, { kind: "pipe", type: AsyncPipe, name: "async" }, { kind: "component", type: WidgetConfigFeedbackComponent, selector: "c8y-widget-config-feedback" }, { kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "pipe", type: C8yTranslatePipe, name: "translate" }, { kind: "directive", type: IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i5.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i5.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i5.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }], viewProviders: [{ provide: ControlContainer, useExisting: NgForm }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: WidgetAssetSelectorComponent, decorators: [{
            type: Component,
            args: [{ selector: 'c8y-widget-asset-selector', host: { class: 'bg-level-1' }, standalone: true, imports: [
                        AssetSelectorModule,
                        AsyncPipe,
                        WidgetConfigFeedbackComponent,
                        NgIf,
                        NgClass,
                        C8yTranslatePipe,
                        IconDirective,
                        FormsModule
                    ], viewProviders: [{ provide: ControlContainer, useExisting: NgForm }], template: "<div class=\"card borderless\">\n  <div\n    class=\"card-block p-0 bg-inherit\"\n    style=\"height: 315px\"\n  >\n    <!-- \n      ngIf is needed to avoid a race condition as \n      asset-selector seems not to support dynamic config changes\n    -->\n    <c8y-asset-selector-miller\n      class=\"d-block bg-inherit p-relative\"\n      *ngIf=\"assetSelectorConfig\"\n      (onSelected)=\"selectionChanged($event)\"\n      [config]=\"assetSelectorConfig\"\n      [asset]=\"(widgetConfigService.currentConfig$ | async).settings?.context\"\n      [(ngModel)]=\"selectedDevice\"\n      [required]=\"isRequired\"\n      name=\"configAsset\"\n    ></c8y-asset-selector-miller>\n  </div>\n\n  <c8y-widget-config-feedback>\n    <span\n      class=\"tag chip text-12 m-4\"\n      [ngClass]=\"{\n        'tag--info': selectedDevice || !isRequired,\n        'tag--danger': !selectedDevice && isRequired\n      }\"\n    >\n      <button\n        class=\"btn-clean text-12 m-r-4\"\n        title=\"{{ 'Deselect' | translate }}\"\n        type=\"button\"\n        *ngIf=\"selectedDevice\"\n        (click)=\"selectedDevice = null; updateConfig(null)\"\n      >\n        <i c8yIcon=\"times\"></i>\n      </button>\n      <span\n        class=\"text-truncate\"\n        title=\"{{ selectedDevice.name || 'Device ' + selectedDevice.id }}\"\n        *ngIf=\"selectedDevice\"\n      >\n        {{ selectedDevice.name || 'Device ' + selectedDevice.id }}\n      </span>\n      <span\n        *ngIf=\"!selectedDevice\"\n        translate\n      >\n        No asset selected\n      </span>\n    </span>\n  </c8y-widget-config-feedback>\n</div>\n" }]
        }], propDecorators: { isRequired: [{
                type: Input
            }], showUnassigned: [{
                type: Input
            }], groupsSelectable: [{
                type: Input
            }], assetSelectorConfig: [{
                type: Input
            }], selectedDevice: [{
                type: Input
            }] } });

class WidgetConfigGeneralComponent {
    constructor() {
        this.widgetConfigService = inject(WidgetConfigService);
    }
    ngAfterViewInit() {
        if (this.dynamicComponent) {
            this.widgetConfigService.selectedComponent$.next(this.dynamicComponent);
            this.widgetConfigService.addOnBeforeSave(() => this.dynamicComponent.callLifeCycleHooks());
        }
    }
    ngOnDestroy() {
        this.widgetConfigService.selectedComponent$.next(null);
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: WidgetConfigGeneralComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.14", type: WidgetConfigGeneralComponent, isStandalone: true, selector: "c8y-widget-config-general", inputs: { definition: "definition" }, viewQueries: [{ propertyName: "dynamicComponent", first: true, predicate: DynamicComponentComponent, descendants: true }], ngImport: i0, template: "<c8y-dynamic-component\n  class=\"d-block p-l-16 p-r-16\"\n  [componentId]=\"definition.id\"\n  mode=\"config\"\n  [config]=\"widgetConfigService.currentConfig$ | async\"\n></c8y-dynamic-component>\n", dependencies: [{ kind: "pipe", type: AsyncPipe, name: "async" }, { kind: "ngmodule", type: DynamicComponentModule }, { kind: "component", type: i2.DynamicComponentComponent, selector: "c8y-dynamic-component", inputs: ["componentId", "config", "mode", "notFoundError", "executeResolvers"], outputs: ["updateWidgetClasses"] }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: WidgetConfigGeneralComponent, decorators: [{
            type: Component,
            args: [{ standalone: true, imports: [AsyncPipe, DynamicComponentModule], selector: 'c8y-widget-config-general', template: "<c8y-dynamic-component\n  class=\"d-block p-l-16 p-r-16\"\n  [componentId]=\"definition.id\"\n  mode=\"config\"\n  [config]=\"widgetConfigService.currentConfig$ | async\"\n></c8y-dynamic-component>\n" }]
        }], propDecorators: { definition: [{
                type: Input
            }], dynamicComponent: [{
                type: ViewChild,
                args: [DynamicComponentComponent]
            }] } });

class WidgetConfigService {
    /**
     * The current configuration of the selected widget.
     * Only to read the value. Use `updateConfig` to update the configuration.
     **/
    get currentConfig() {
        return this._currentConfig$.value;
    }
    constructor(widgetConfigSectionService) {
        this.widgetConfigSectionService = widgetConfigSectionService;
        this._currentConfig$ = new BehaviorSubject(null);
        /**
         * The preview of the currently selected widget. If `null` no preview is shown
         * (except if the selected widget has no configuration).
         */
        this.preview$ = new BehaviorSubject(null);
        /**
         * The currently selected widget definition.
         */
        this.selected$ = new BehaviorSubject(null);
        /**
         * The currently selected widget component.
         */
        this.selectedComponent$ = new BehaviorSubject(null);
        /**
         * The current configuration of the selected widget as observable.
         * Only to read the value. Use `updateConfig` to update the configuration.
         */
        this.currentConfig$ = this._currentConfig$.asObservable();
        /**
         * Returns all the current sections.
         *
         * A configuration section is a component that can be used to configure a widget.
         * Sections should be added via the `hookWidgetConfig`.
         */
        this.currentSections$ = this.selected$.pipe(tap$1(() => (this.beforeSaveCallbacks = [])), withLatestFrom(this._currentConfig$), mergeMap$1(([selectedWidget, config]) => selectedWidget === null
            ? of([])
            : this.widgetConfigSectionService.items$.pipe(map$1(sections => [
                ...this.getWidgetDefaultSections(selectedWidget, config),
                ...sections.filter(section => section.widgetId === selectedWidget.id)
            ]))), mergeMap$1((sections) => {
            if (sections.length === 0) {
                return Promise.resolve([]);
            }
            return Promise.all(sections.map(async (section) => ({
                component: await section.loadComponent(),
                label: section.label,
                priority: section.priority || 0,
                expanded: section.expanded || false,
                initialState: section.initialState,
                injector: section.injector,
                providers: section.providers
            })));
        }), map$1(sections => sortByPriority(sections)), shareReplay$1(1));
        /**
         * Returns a list of all unique providers used in the current sections.
         */
        this.providers$ = this.currentSections$.pipe(map$1(sections => Array.from(new Set(sections.flatMap(section => section.providers || [])))));
        /**
         * Indicates if the current selected widget has a configuration or not.
         */
        this.hasConfig$ = this.currentSections$.pipe(map$1(sections => sections.length > 0));
        /**
         * The view state of the preview. Can be one of:
         * - `noPreview`: No preview is shown and an empty state is shown.
         * - `configPreview`: The preview is shown with the view component of the selected widget.
         * - `templatePreview`: The preview is shown with any template passed with setPreview method.
         */
        this.previewViewState$ = combineLatest([this.hasConfig$, this.preview$]).pipe(map$1(([hasConfig, preview]) => {
            if (preview === null && hasConfig) {
                return 'noPreview';
            }
            else if (preview instanceof TemplateRef) {
                return 'templatePreview';
            }
            else if (preview === true || !hasConfig) {
                return 'configPreview';
            }
        }));
        this.STASH_CONFIG_DELAY = 300;
        this.configChange$ = new Subject();
        this.configChangeReset$ = new Subject();
        this.beforeSaveCallbacks = [];
        const add = value => state => ({ ...state, ...value });
        const clear = () => _state => ({});
        merge(this.configChange$.pipe(map$1(add)), this.configChangeReset$.pipe(map$1(clear)))
            .pipe(scan((state, innerFn) => innerFn(state), {}), debounceTime(this.STASH_CONFIG_DELAY))
            .subscribe(config => {
            if (!this._currentConfig$.value) {
                return;
            }
            Object.assign(this._currentConfig$.value, config);
            if (this.selectedComponent$.value) {
                this.selectedComponent$.value.emitConfigChange(this._currentConfig$.value);
            }
        });
    }
    /**
     * Use this function to get a typed configuration of the selected widget.
     * @returns The current configuration of the selected widget or an empty object if no config is defined.
     */
    getWidgetConfig() {
        return this.currentConfig$.pipe(map$1(current => {
            return (current.config || {});
        }));
    }
    /**
     * Adds a callback which can check the configuration before saving.
     */
    addOnBeforeSave(callback) {
        this.beforeSaveCallbacks.push(callback);
    }
    /**
     * Returns true if the configuration can be saved.
     */
    canSave() {
        if (this.beforeSaveCallbacks.length === 0) {
            return Promise.resolve(true);
        }
        const config = this._currentConfig$.value;
        return combineLatest(this.beforeSaveCallbacks.map(cb => {
            const result = cb(config);
            if (isObservable(result)) {
                return result;
            }
            if (isPromise(result)) {
                return from(result);
            }
            return of(result);
        }))
            .pipe(map$1(results => results.every(result => result === true)))
            .toPromise();
    }
    /**
     * Updates the configuration of the selected widget.
     * @param change The change to apply to the configuration.
     * @param force If set to true, the configuration will be updated even if no changes are made. Also concurrency checks are ignored and no change is emitted on the component.
     *              Use with caution and only if you want to reset the configuration to some default.
     */
    updateConfig(change, force = false) {
        if (force) {
            this._currentConfig$.next({ ...this._currentConfig$.value, ...change });
            return;
        }
        this.configChange$.next(change);
    }
    /**
     * Used to initialize the configuration of the selected widget.
     * @param config The configuration to set.
     */
    initConfig(config) {
        this._currentConfig$.next(config);
    }
    /**
     * Select a widget
     * @param selected The widget to select.
     */
    selectWidget(selected) {
        this.selected$.next(selected);
    }
    /**
     * Deselect the currently selected.
     */
    deselectWidget() {
        this.selected$.next(null);
        this.preview$.next(null);
        this.configChangeReset$.next();
        this._currentConfig$.next(null);
    }
    /**
     * Enables a preview.
     * @param preview If set to true, the view component of the current widget will be taken to render the preview.
     *                If set to a DynamicComponentDefinition, the preview will be rendered with the given component.
     *                If set to a TemplateRef, the preview will be rendered with the given template.
     */
    setPreview(preview) {
        this.preview$.next(preview);
    }
    /**
     * By default, no sections are added to the configuration. However, to ensure
     * backwards compatibility with old plugins, we check if the default asset selector
     * and config section should be shown.
     * @param selectedWidget The currently selected widget.
     * @param config The used widget configuration.
     * @returns The default sections.
     */
    getWidgetDefaultSections(selectedWidget, config) {
        const defaultWidgetConfigSections = [];
        if (this.isDefaultSettingSectionNeeded(selectedWidget)) {
            defaultWidgetConfigSections.push({
                widgetId: selectedWidget.id,
                label: gettext('Settings'),
                loadComponent: () => Promise.resolve(WidgetConfigGeneralComponent),
                initialState: {
                    definition: selectedWidget
                },
                priority: 0,
                expanded: true
            });
        }
        // Adds a default device selector for old plugins
        if (this.isDefaultDeviceSelectorNeeded(selectedWidget, config)) {
            defaultWidgetConfigSections.push({
                widgetId: selectedWidget.id,
                label: gettext('Asset selection'),
                loadComponent: () => Promise.resolve(WidgetAssetSelectorComponent),
                priority: 100,
                initialState: {
                    isRequired: !config.settings?.ng1?.options?.deviceTargetNotRequired &&
                        !config.settings.deviceTargetNotRequired
                }
            });
        }
        return defaultWidgetConfigSections;
    }
    /**
     * Sections should not be added by default anymore. But to ensure backwards compatibility, we
     * check if the default selector should be shown. We assume that a default selector will be shown
     * when:
     *  1. The widget has a dynamic component for configuration (which is also deprecated).
     *  OR
     *  2. The widget has not disabled the default selector.
     */
    isDefaultDeviceSelectorNeeded(selectedWidget, config) {
        if (!selectedWidget.loadConfigComponent && !selectedWidget.configComponent) {
            return false;
        }
        return (!config.settings?.noDeviceTarget &&
            !config.settings?.ng1?.options?.noDeviceTarget &&
            !config.settings?.hideTarget);
    }
    /**
     * Checks if a default setting section is needed.
     * @param selectedWidget The currently selected widget.
     * @returns true if one is needed.
     */
    isDefaultSettingSectionNeeded(selectedWidget) {
        if (selectedWidget.data?.settings?.upgrade) {
            return (selectedWidget.data?.settings?.configComponent ||
                selectedWidget.data?.settings?.configTemplateUrl);
        }
        return selectedWidget.configComponent || selectedWidget.loadConfigComponent;
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: WidgetConfigService, deps: [{ token: WidgetConfigSectionService }], target: i0.ɵɵFactoryTarget.Injectable }); }
    static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: WidgetConfigService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: WidgetConfigService, decorators: [{
            type: Injectable,
            args: [{
                    providedIn: 'root'
                }]
        }], ctorParameters: () => [{ type: WidgetConfigSectionService }] });

class WidgetConfigRootComponent {
    constructor() {
        this.widgetConfigService = inject(WidgetConfigService);
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: WidgetConfigRootComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.14", type: WidgetConfigRootComponent, isStandalone: true, selector: "c8y-widget-config-root", ngImport: i0, template: "<ng-container\n  *ngFor=\"let section of widgetConfigService.currentSections$ | async\"\n>\n  <c8y-widget-config-section [section]=\"section\"></c8y-widget-config-section>\n</ng-container>\n", dependencies: [{ kind: "directive", type: NgFor, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "component", type: WidgetConfigSectionComponent, selector: "c8y-widget-config-section", inputs: ["section"] }, { kind: "pipe", type: AsyncPipe, name: "async" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: WidgetConfigRootComponent, decorators: [{
            type: Component,
            args: [{ standalone: true, imports: [NgFor, WidgetConfigSectionComponent, AsyncPipe], selector: 'c8y-widget-config-root', template: "<ng-container\n  *ngFor=\"let section of widgetConfigService.currentSections$ | async\"\n>\n  <c8y-widget-config-section [section]=\"section\"></c8y-widget-config-section>\n</ng-container>\n" }]
        }] });

class WidgetService {
    constructor(dynamicComponentService, translateService, moduleConfig) {
        this.dynamicComponentService = dynamicComponentService;
        this.translateService = translateService;
        this.moduleConfig = moduleConfig;
        this.dynamicComponentService.items$.subscribe(widgets => {
            this.widgets = widgets;
        });
    }
    async getWidgetDefinition(componentId) {
        return this.dynamicComponentService.getById(componentId);
    }
    getWidgetDefinitions() {
        const translatedComponents = this.widgets.map(cmp => ({
            ...cmp,
            label: this.translateService.instant(cmp.label)
        }));
        // cloneDeep and injector lead to a performance issue. We removing the injectors here,
        // as they are not needed, to avoid use of a lot of CPU for cloning.
        const translatedComponentsWithoutInjector = translatedComponents.map(cmp => omit(cmp, 'injector'));
        const filterFn = this.moduleConfig?.widgetFilter ||
            (({ data }) => !(data && data.settings && data.settings.noNewWidgets));
        return cloneDeep(sortBy(translatedComponentsWithoutInjector, 'label').filter(filterFn));
    }
    async mapLegacy(widget) {
        const cmp = await this.getWidgetDefinition(widget.componentId || widget.name);
        widget = this.mapDisplaySettings(widget, cmp);
        if (get(cmp, 'data.settings.upgrade')) {
            widget.widgetComponent = cmp.data.settings.widgetComponent;
            widget.configComponent = cmp.data.settings.configComponent;
            widget.templateUrl = cmp.data.settings.templateUrl;
            widget.configTemplateUrl = cmp.data.settings.configTemplateUrl;
            widget.transformConfigWithContext =
                cmp.data.settings.componentTransformConfigWithContext ||
                    cmp.data.settings.transformConfigWithContext ||
                    widget.transformConfigWithContext;
        }
        else {
            delete widget.templateUrl;
            delete widget.configTemplateUrl;
        }
        return widget;
    }
    mapDisplaySettings(widget, cmp) {
        if (cmp?.data?.displaySettings) {
            widget.config = { displaySettings: cmp.data.displaySettings, ...(widget.config || {}) };
        }
        return widget;
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: WidgetService, deps: [{ token: i2.DynamicComponentService }, { token: i2$1.TranslateService }, { token: CONTEXT_DASHBOARD_CONFIG, optional: true }], target: i0.ɵɵFactoryTarget.Injectable }); }
    static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: WidgetService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: WidgetService, decorators: [{
            type: Injectable,
            args: [{
                    providedIn: 'root'
                }]
        }], ctorParameters: () => [{ type: i2.DynamicComponentService }, { type: i2$1.TranslateService }, { type: undefined, decorators: [{
                    type: Optional
                }, {
                    type: Inject,
                    args: [CONTEXT_DASHBOARD_CONFIG]
                }] }] });

class WidgetPreviewComponent {
    constructor() {
        this.widgetConfigService = inject(WidgetConfigService);
        this.previewClasses = {
            'dashboard-theme-light': true,
            'panel-title-regular': true
        };
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: WidgetPreviewComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.14", type: WidgetPreviewComponent, isStandalone: true, selector: "c8y-widget-preview", inputs: { previewClasses: "previewClasses" }, ngImport: i0, template: "<fieldset class=\"c8y-fieldset p-l-24 p-r-24 p-t-16 p-b-16\">\n  <legend>{{ 'Preview' | translate }}</legend>\n  <c8y-dashboard\n    class=\"dashboard-preview-slot\"\n    [columns]=\"1\"\n    [ngClass]=\"previewClasses\"\n  >\n    <c8y-dashboard-child\n      [title]=\"(widgetConfigService.selected$ | async)?.data?.title\"\n      [isFrozen]=\"true\"\n      [width]=\"1\"\n      [height]=\"6\"\n    >\n      <c8y-dashboard-child-title>\n        <span translate>{{ (widgetConfigService.selected$ | async)?.data?.title }}</span>\n      </c8y-dashboard-child-title>\n\n      <div\n        class=\"fit-h d-flex d-col j-c-center a-i-center\"\n        *ngIf=\"(widgetConfigService.previewViewState$ | async) === 'noPreview'\"\n      >\n        <c8y-ui-empty-state\n          [icon]=\"'visibility-off'\"\n          [title]=\"'No preview available.' | translate\"\n          [subtitle]=\"'This widget does not provide any preview.' | translate\"\n          [horizontal]=\"false\"\n        ></c8y-ui-empty-state>\n      </div>\n\n      <c8y-dynamic-component\n        *ngIf=\"(widgetConfigService.previewViewState$ | async) === 'configPreview'\"\n        [componentId]=\"(widgetConfigService.selected$ | async)?.id\"\n        [config]=\"(widgetConfigService.currentConfig$ | async)?.config\"\n      ></c8y-dynamic-component>\n\n      <ng-template\n        *ngIf=\"(widgetConfigService.previewViewState$ | async) === 'templatePreview'\"\n        [ngTemplateOutlet]=\"widgetConfigService.preview$ | async\"\n      ></ng-template>\n    </c8y-dashboard-child>\n  </c8y-dashboard>\n</fieldset>\n", dependencies: [{ kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "pipe", type: C8yTranslatePipe, name: "translate" }, { kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i3.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "pipe", type: i3.AsyncPipe, name: "async" }, { kind: "ngmodule", type: CoreModule }, { kind: "component", type: i2.EmptyStateComponent, selector: "c8y-ui-empty-state", inputs: ["icon", "title", "subtitle", "horizontal"] }, { kind: "directive", type: i2.C8yTranslateDirective, selector: "[translate],[ngx-translate]" }, { kind: "component", type: i2.DynamicComponentComponent, selector: "c8y-dynamic-component", inputs: ["componentId", "config", "mode", "notFoundError", "executeResolvers"], outputs: ["updateWidgetClasses"] }, { kind: "component", type: i2.DashboardComponent, selector: "c8y-dashboard", inputs: ["columns", "gap", "rows"], outputs: ["dashboardChange"] }, { kind: "component", type: i2.DashboardChildComponent, selector: "c8y-dashboard-child", inputs: ["x", "y", "width", "height", "data", "margin", "useIntersection", "isFrozen", "canToggleFullscreen", "editMode", "class"], outputs: ["changeStart", "changeEnd", "toggleFullscreen"] }, { kind: "component", type: i2.DashboardChildTitleComponent, selector: "c8y-dashboard-child-title" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: WidgetPreviewComponent, decorators: [{
            type: Component,
            args: [{ selector: 'c8y-widget-preview', standalone: true, imports: [NgIf, NgClass, C8yTranslatePipe, CommonModule, CoreModule], template: "<fieldset class=\"c8y-fieldset p-l-24 p-r-24 p-t-16 p-b-16\">\n  <legend>{{ 'Preview' | translate }}</legend>\n  <c8y-dashboard\n    class=\"dashboard-preview-slot\"\n    [columns]=\"1\"\n    [ngClass]=\"previewClasses\"\n  >\n    <c8y-dashboard-child\n      [title]=\"(widgetConfigService.selected$ | async)?.data?.title\"\n      [isFrozen]=\"true\"\n      [width]=\"1\"\n      [height]=\"6\"\n    >\n      <c8y-dashboard-child-title>\n        <span translate>{{ (widgetConfigService.selected$ | async)?.data?.title }}</span>\n      </c8y-dashboard-child-title>\n\n      <div\n        class=\"fit-h d-flex d-col j-c-center a-i-center\"\n        *ngIf=\"(widgetConfigService.previewViewState$ | async) === 'noPreview'\"\n      >\n        <c8y-ui-empty-state\n          [icon]=\"'visibility-off'\"\n          [title]=\"'No preview available.' | translate\"\n          [subtitle]=\"'This widget does not provide any preview.' | translate\"\n          [horizontal]=\"false\"\n        ></c8y-ui-empty-state>\n      </div>\n\n      <c8y-dynamic-component\n        *ngIf=\"(widgetConfigService.previewViewState$ | async) === 'configPreview'\"\n        [componentId]=\"(widgetConfigService.selected$ | async)?.id\"\n        [config]=\"(widgetConfigService.currentConfig$ | async)?.config\"\n      ></c8y-dynamic-component>\n\n      <ng-template\n        *ngIf=\"(widgetConfigService.previewViewState$ | async) === 'templatePreview'\"\n        [ngTemplateOutlet]=\"widgetConfigService.preview$ | async\"\n      ></ng-template>\n    </c8y-dashboard-child>\n  </c8y-dashboard>\n</fieldset>\n" }]
        }], propDecorators: { previewClasses: [{
                type: Input
            }] } });

class AppearanceSettingsComponent {
    constructor() {
        this.themeClass = 'dashboard-theme-light';
        this.headerClass = 'panel-title-regular';
        this.themeClassChange = new EventEmitter();
        this.headerClassChange = new EventEmitter();
        this.onChange = new EventEmitter();
        this.possibleStylingTheme = DASHBOARD_THEME_CLASSES;
        this.possibleStylingHeader = WIDGET_HEADER_CLASSES;
        this.columns = 1;
        this.dashboardDefaultLabel = gettext('Dashboard default');
    }
    themeClassClick(value) {
        value = this.resetToDefault(value, this.themeClass, this.defaultThemeClass);
        this.themeClass = value;
        this.themeClassChange.emit(value);
        this.onChange.emit();
    }
    headerClassClick(value) {
        value = this.resetToDefault(value, this.headerClass, this.defaultHeaderClass);
        this.headerClass = value;
        this.headerClassChange.emit(value);
        this.onChange.emit();
    }
    resetToDefault(value, compareTo, defaultValue) {
        if (defaultValue && value === compareTo) {
            value = defaultValue;
        }
        return value;
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AppearanceSettingsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.14", type: AppearanceSettingsComponent, isStandalone: true, selector: "c8y-appearance-settings", inputs: { themeClass: "themeClass", headerClass: "headerClass", defaultThemeClass: "defaultThemeClass", defaultHeaderClass: "defaultHeaderClass", dashboardSettings: "dashboardSettings", possibleStylingTheme: "possibleStylingTheme", possibleStylingHeader: "possibleStylingHeader", columns: "columns" }, outputs: { themeClassChange: "themeClassChange", headerClassChange: "headerClassChange", onChange: "onChange" }, ngImport: i0, template: "<div class=\"row\">\n  <div class=\"col-lg-{{ 12 / columns }} col-xs-12\">\n    <fieldset class=\"c8y-fieldset c8y-fieldset--lg\">\n      <legend *ngIf=\"dashboardSettings\">{{ 'Default theme' | translate }}</legend>\n      <legend *ngIf=\"!dashboardSettings\">{{ 'Theme' | translate }}</legend>\n      <ul class=\"list-group\">\n        <li\n          class=\"list-group-item d-flex a-i-center p-l-0 p-r-0 fit-w\"\n          *ngFor=\"let themeClassItem of possibleStylingTheme; let i = index\"\n        >\n          <div\n            class=\"list-item-checkbox\"\n            style=\"max-width: calc(100% - 24px)\"\n          >\n            <label class=\"c8y-radio\">\n              <input\n                name=\"content\"\n                type=\"radio\"\n                [id]=\"'groupradiocontentclass' + i\"\n                [value]=\"themeClassItem.class\"\n                [ngModel]=\"themeClass\"\n                (click)=\"themeClassClick(themeClassItem.class)\"\n              />\n              <span></span>\n              <span\n                class=\"text-truncate\"\n                title=\"{{ themeClassItem.label | translate }}{{\n                  themeClassItem.class === defaultThemeClass\n                    ? ' | ' + (dashboardDefaultLabel | translate)\n                    : ''\n                }}\"\n              >\n                <span>{{ themeClassItem.label | translate }}</span>\n                <br />\n                <small\n                  class=\"text-muted\"\n                  *ngIf=\"themeClassItem.class === defaultThemeClass\"\n                >\n                  {{ dashboardDefaultLabel | translate }}\n                </small>\n              </span>\n            </label>\n          </div>\n\n          <button\n            class=\"btn-help btn-help--sm m-l-auto\"\n            [attr.aria-label]=\"'Help' | translate\"\n            popover=\"{{ themeClassItem.description | translate }}\"\n            placement=\"right\"\n            triggers=\"focus\"\n            container=\"body\"\n            type=\"button\"\n          ></button>\n        </li>\n      </ul>\n    </fieldset>\n  </div>\n  <div class=\"col-lg-{{ 12 / columns }} col-xs-12\">\n    <fieldset class=\"c8y-fieldset c8y-fieldset--lg\">\n      <legend *ngIf=\"dashboardSettings\">\n        {{ 'Default widget header style' | translate }}\n      </legend>\n      <legend *ngIf=\"!dashboardSettings\">\n        {{ 'Widget header style' | translate }}\n      </legend>\n      <ul class=\"list-group\">\n        <li\n          class=\"list-group-item d-flex a-i-center p-l-0 p-r-0\"\n          *ngFor=\"let headerClassItem of possibleStylingHeader; let i = index\"\n        >\n          <div\n            class=\"list-item-checkbox\"\n            style=\"max-width: calc(100% - 24px)\"\n          >\n            <label class=\"c8y-radio\">\n              <input\n                name=\"header\"\n                type=\"radio\"\n                [id]=\"'groupradioheaderclass' + i\"\n                [value]=\"headerClassItem.class\"\n                [ngModel]=\"headerClass\"\n                (click)=\"headerClassClick(headerClassItem.class)\"\n              />\n              <span></span>\n              <span\n                class=\"text-truncate\"\n                title=\"{{ headerClassItem.label | translate }}{{\n                  headerClassItem.class === defaultHeaderClass\n                    ? ' | ' + (dashboardDefaultLabel | translate)\n                    : ''\n                }}\"\n              >\n                <span>{{ headerClassItem.label | translate }}</span>\n                <br />\n                <small\n                  class=\"text-muted\"\n                  *ngIf=\"headerClassItem.class === defaultHeaderClass\"\n                >\n                  {{ dashboardDefaultLabel | translate }}\n                </small>\n              </span>\n            </label>\n          </div>\n          <button\n            class=\"btn-help btn-help--sm m-l-auto\"\n            [attr.aria-label]=\"'Help' | translate\"\n            popover=\"{{ headerClassItem.description | translate }}\"\n            placement=\"right\"\n            triggers=\"focus\"\n            container=\"body\"\n            type=\"button\"\n          ></button>\n        </li>\n      </ul>\n    </fieldset>\n  </div>\n  <ng-content></ng-content>\n</div>\n", dependencies: [{ kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i5.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i5.RadioControlValueAccessor, selector: "input[type=radio][formControlName],input[type=radio][formControl],input[type=radio][ngModel]", inputs: ["name", "formControlName", "value"] }, { kind: "directive", type: i5.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i5.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: PopoverModule }, { kind: "directive", type: i2$2.PopoverDirective, selector: "[popover]", inputs: ["adaptivePosition", "boundariesElement", "popover", "popoverContext", "popoverTitle", "placement", "outsideClick", "triggers", "container", "containerClass", "isOpen", "delay"], outputs: ["onShown", "onHidden"], exportAs: ["bs-popover"] }, { kind: "pipe", type: C8yTranslatePipe, name: "translate" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AppearanceSettingsComponent, decorators: [{
            type: Component,
            args: [{ selector: 'c8y-appearance-settings', standalone: true, imports: [NgIf, NgForOf, FormsModule, PopoverModule, C8yTranslatePipe], template: "<div class=\"row\">\n  <div class=\"col-lg-{{ 12 / columns }} col-xs-12\">\n    <fieldset class=\"c8y-fieldset c8y-fieldset--lg\">\n      <legend *ngIf=\"dashboardSettings\">{{ 'Default theme' | translate }}</legend>\n      <legend *ngIf=\"!dashboardSettings\">{{ 'Theme' | translate }}</legend>\n      <ul class=\"list-group\">\n        <li\n          class=\"list-group-item d-flex a-i-center p-l-0 p-r-0 fit-w\"\n          *ngFor=\"let themeClassItem of possibleStylingTheme; let i = index\"\n        >\n          <div\n            class=\"list-item-checkbox\"\n            style=\"max-width: calc(100% - 24px)\"\n          >\n            <label class=\"c8y-radio\">\n              <input\n                name=\"content\"\n                type=\"radio\"\n                [id]=\"'groupradiocontentclass' + i\"\n                [value]=\"themeClassItem.class\"\n                [ngModel]=\"themeClass\"\n                (click)=\"themeClassClick(themeClassItem.class)\"\n              />\n              <span></span>\n              <span\n                class=\"text-truncate\"\n                title=\"{{ themeClassItem.label | translate }}{{\n                  themeClassItem.class === defaultThemeClass\n                    ? ' | ' + (dashboardDefaultLabel | translate)\n                    : ''\n                }}\"\n              >\n                <span>{{ themeClassItem.label | translate }}</span>\n                <br />\n                <small\n                  class=\"text-muted\"\n                  *ngIf=\"themeClassItem.class === defaultThemeClass\"\n                >\n                  {{ dashboardDefaultLabel | translate }}\n                </small>\n              </span>\n            </label>\n          </div>\n\n          <button\n            class=\"btn-help btn-help--sm m-l-auto\"\n            [attr.aria-label]=\"'Help' | translate\"\n            popover=\"{{ themeClassItem.description | translate }}\"\n            placement=\"right\"\n            triggers=\"focus\"\n            container=\"body\"\n            type=\"button\"\n          ></button>\n        </li>\n      </ul>\n    </fieldset>\n  </div>\n  <div class=\"col-lg-{{ 12 / columns }} col-xs-12\">\n    <fieldset class=\"c8y-fieldset c8y-fieldset--lg\">\n      <legend *ngIf=\"dashboardSettings\">\n        {{ 'Default widget header style' | translate }}\n      </legend>\n      <legend *ngIf=\"!dashboardSettings\">\n        {{ 'Widget header style' | translate }}\n      </legend>\n      <ul class=\"list-group\">\n        <li\n          class=\"list-group-item d-flex a-i-center p-l-0 p-r-0\"\n          *ngFor=\"let headerClassItem of possibleStylingHeader; let i = index\"\n        >\n          <div\n            class=\"list-item-checkbox\"\n            style=\"max-width: calc(100% - 24px)\"\n          >\n            <label class=\"c8y-radio\">\n              <input\n                name=\"header\"\n                type=\"radio\"\n                [id]=\"'groupradioheaderclass' + i\"\n                [value]=\"headerClassItem.class\"\n                [ngModel]=\"headerClass\"\n                (click)=\"headerClassClick(headerClassItem.class)\"\n              />\n              <span></span>\n              <span\n                class=\"text-truncate\"\n                title=\"{{ headerClassItem.label | translate }}{{\n                  headerClassItem.class === defaultHeaderClass\n                    ? ' | ' + (dashboardDefaultLabel | translate)\n                    : ''\n                }}\"\n              >\n                <span>{{ headerClassItem.label | translate }}</span>\n                <br />\n                <small\n                  class=\"text-muted\"\n                  *ngIf=\"headerClassItem.class === defaultHeaderClass\"\n                >\n                  {{ dashboardDefaultLabel | translate }}\n                </small>\n              </span>\n            </label>\n          </div>\n          <button\n            class=\"btn-help btn-help--sm m-l-auto\"\n            [attr.aria-label]=\"'Help' | translate\"\n            popover=\"{{ headerClassItem.description | translate }}\"\n            placement=\"right\"\n            triggers=\"focus\"\n            container=\"body\"\n            type=\"button\"\n          ></button>\n        </li>\n      </ul>\n    </fieldset>\n  </div>\n  <ng-content></ng-content>\n</div>\n" }]
        }], propDecorators: { themeClass: [{
                type: Input
            }], headerClass: [{
                type: Input
            }], defaultThemeClass: [{
                type: Input
            }], defaultHeaderClass: [{
                type: Input
            }], dashboardSettings: [{
                type: Input
            }], themeClassChange: [{
                type: Output
            }], headerClassChange: [{
                type: Output
            }], onChange: [{
                type: Output
            }], possibleStylingTheme: [{
                type: Input
            }], possibleStylingHeader: [{
                type: Input
            }], columns: [{
                type: Input
            }] } });

class WidgetConfigComponent {
    get isEdit() {
        return !!this.current;
    }
    get isDeviceTypeDashboard() {
        return !!this.mo.c8y_Dashboard?.deviceType && !!this.mo.c8y_Dashboard?.deviceTypeValue;
    }
    constructor(widgetService, bottomDrawerRef, contextDashboardService, widgetConfigService) {
        this.widgetService = widgetService;
        this.bottomDrawerRef = bottomDrawerRef;
        this.contextDashboardService = contextDashboardService;
        this.widgetConfigService = widgetConfigService;
        this.searchChange$ = new Subject();
        this.searchTerm = '';
        this.styling = {
            headerClass: 'panel-title-regular',
            contentClass: 'panel-content-light'
        };
        this.defaultStyling = {
            headerClass: 'panel-title-regular',
            contentClass: 'panel-content-light'
        };
        this.isUpgrade = false;
        this.possibleStyling = { WIDGET_HEADER_CLASSES, WIDGET_CONTENT_CLASSES };
        /**
         * Used as wrapper component to inject providers.
         */
        this.widgetConfigRoot = WidgetConfigRootComponent;
        this.result = new Promise((resolve, reject) => {
            this._save = resolve;
            this._cancel = reject;
        });
    }
    async ngAfterContentInit() {
        this.components = this.widgetService.getWidgetDefinitions();
        if (this.selected) {
            this.current = clone(this.selected);
            this.select(this.selected, this.isEdit ? 'config' : 'select');
        }
        this.searchSub = this.searchChange$
            .pipe(switchMap((event) => iif(() => event.which !== 13, timer(200), timer(10))))
            .subscribe(() => {
            this.search();
        });
        this.setStylings();
    }
    isSaveDisabled() {
        // If new section concept, the widget settings cannot be used to disable the device target.
        // Use the WidgetConfigAssetTargetComponent instead with the initialState set to isRequired.
        const isNewSectionConcept = !this.selected?.loadConfigComponent && !this.selected?.configComponent;
        return this.configForm?.invalid || (!isNewSectionConcept && this.checkIfDeviceRequired());
    }
    checkIfDeviceRequired() {
        const widgetConfig = this.widgetConfigService.currentConfig;
        return (!widgetConfig ||
            (!widgetConfig.settings?.deviceTargetNotRequired &&
                !widgetConfig.device &&
                !widgetConfig.settings?.noDeviceTarget));
    }
    async save() {
        const canSave = await this.widgetConfigService.canSave();
        if (!canSave) {
            return;
        }
        const { _x, _y, _width, _height } = this.selected.data;
        if (this.widgetConfig &&
            this.widgetConfig.device &&
            // serializing will be handled by resolver
            !this.selected?.resolve?.device) {
            const { id, name } = this.widgetConfig.device;
            this.widgetConfig.device = { id, name };
        }
        this.widgetConfig = {
            ...this.widgetConfig,
            ...this.widgetConfigService.currentConfig
        };
        const widget = {
            _x,
            _y,
            _width,
            _height,
            config: omit(this.widgetConfig, ['settings', 'displaySettings']),
            title: this.selected.data.title,
            componentId: this.selected.id,
            id: this.isEdit ? this.current.data.id : String(Math.random()).substr(2),
            classes: this.getStyle(),
            ...(!this.isEdit ? this.widgetConfig.settings.widgetDefaults : {})
        };
        this._save(widget);
        await this.bottomDrawerRef.close();
        this.widgetConfigService.deselectWidget();
    }
    select(cmp, mode = 'config') {
        if (mode === 'config' && this.configForm) {
            const formGroup = this.configForm.control;
            for (const control of Object.keys(formGroup.controls)) {
                formGroup.removeControl(control);
            }
        }
        cmp.data = cmp.data || {};
        this.selected = cmp;
        this.isUpgrade = !!get(cmp, 'data.settings.upgrade');
        this.contextDashboardService.formDisabled = this.isUpgrade;
        if (this.isEdit) {
            const { _x, _y, _width, _height, classes, title } = this.current.data;
            this.selected.data = { ...this.selected.data, _x, _y, _width, _height, classes, title };
        }
        this.widgetConfig = cloneDeep(this.composeWidgetConfig(this.selected, this.context));
        this.selected.data.title = this.selected.data.title || cmp.label;
        this.componentLabel = cmp.label;
        this.widgetConfigService.initConfig(this.widgetConfig);
        this.widgetConfigService.selectWidget(this.selected);
    }
    search() {
        if (this.searchTerm.length > 0) {
            this.searchResult = this.components.filter(cmp => new RegExp(escapeRegExp(this.searchTerm.trim()), 'i').test(cmp.label));
        }
        else {
            this.resetSearch();
        }
    }
    resetSearch() {
        this.searchTerm = '';
        this.searchResult = undefined;
    }
    backToWidgetSelection() {
        this.selected = null;
        this.widgetConfigService.deselectWidget();
    }
    async close() {
        this._cancel();
        await this.bottomDrawerRef.close();
        this.widgetConfigService.deselectWidget();
    }
    getStyle(isPreview = false) {
        const cssClasses = {};
        if (isPreview || !this.isDashboardDefaultStyle(this.styling.headerClass)) {
            cssClasses[this.styling.headerClass] = true;
        }
        if (isPreview || !this.isDashboardDefaultStyle(this.styling.contentClass)) {
            cssClasses[this.styling.contentClass] = true;
        }
        if (isPreview) {
            cssClasses[`dashboard-theme-${this.defaultStyling.contentClass.split('-').pop()}`] = true;
        }
        return cssClasses;
    }
    ngOnDestroy() {
        this.contextDashboardService.formDisabled = true;
        if (this.searchSub) {
            this.searchSub.unsubscribe();
        }
    }
    setStylings() {
        const dashboardClasses = (this.mo.c8y_Dashboard?.classes && Object.keys(this.mo.c8y_Dashboard.classes)) || [];
        const dashboardWidgetClasses = (this.mo.c8y_Dashboard?.widgetClasses && Object.keys(this.mo.c8y_Dashboard.widgetClasses)) ||
            [];
        const widgetClasses = this.isEdit ? Object.keys(this.current.data.classes) : [];
        this.styling = this.setDefaultStyle([
            ...dashboardClasses,
            ...dashboardWidgetClasses,
            ...widgetClasses
        ]);
        this.defaultStyling = this.setDefaultStyle([...dashboardClasses, ...dashboardWidgetClasses]);
    }
    isDashboardDefaultStyle(className) {
        const allClasses = {
            ...this.mo.c8y_Dashboard.classes,
            ...this.mo.c8y_Dashboard.widgetClasses
        };
        const styles = Object.keys(allClasses).map(cssClass => ({ class: cssClass }));
        const style = this.contextDashboardService.getStyling(styles, className.split('-').pop(), undefined);
        return !!style;
    }
    setDefaultStyle(setClasses) {
        let contentClass = this.styling.contentClass;
        let headerClass = this.styling.headerClass;
        const styles = this.contextDashboardService
            .getFilteredDashboardStyles(setClasses)
            .map(c => c.split('-').pop());
        styles.forEach(styleName => {
            contentClass = this.contextDashboardService.getStyling(WIDGET_CONTENT_CLASSES, styleName, contentClass);
            headerClass = this.contextDashboardService.getStyling(WIDGET_HEADER_CLASSES, styleName, headerClass);
        });
        return { headerClass, contentClass };
    }
    composeWidgetConfig(selectedComponent, context = {}) {
        const setting = {
            settings: {
                ...selectedComponent.data.settings,
                ...get(selectedComponent.data.settings, 'ng1.options'),
                ...get(selectedComponent.data, 'ng1.options'),
                context,
                dashboardMo: this.mo.c8y_Dashboard
            },
            ...selectedComponent.data.config
        };
        return this.applyTargetIfDeviceDashboard(setting);
    }
    applyTargetIfDeviceDashboard(widgetConfig) {
        const isDeviceType = this.contextDashboardService.isDeviceType(this.mo);
        const noDeviceTarget = widgetConfig.settings.ng1
            ? widgetConfig.settings.ng1.options.noDeviceTarget
            : widgetConfig.settings.noDeviceTarget;
        if (isDeviceType) {
            widgetConfig.settings.hideTarget = isDeviceType;
            if (!noDeviceTarget) {
                widgetConfig.device = {
                    id: this.context.id,
                    name: this.context.name
                };
            }
        }
        else if (this.context?.id && !noDeviceTarget) {
            // if the widget is a new widget, we assign the current context
            // as well to have the device available on init of the component
            widgetConfig.device = {
                id: this.context.id,
                name: this.context.name
            };
        }
        return widgetConfig;
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: WidgetConfigComponent, deps: [{ token: WidgetService }, { token: i2.BottomDrawerRef }, { token: ContextDashboardService }, { token: WidgetConfigService }], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.14", type: WidgetConfigComponent, isStandalone: true, selector: "c8y-widget-config", host: { classAttribute: "d-contents" }, viewQueries: [{ propertyName: "configForm", first: true, predicate: ["configForm"], descendants: true }], ngImport: i0, template: "<!-- select widget -->\n<ng-container *ngIf=\"!(widgetConfigService.selected$ | async)\">\n  <div class=\"card-header j-c-center separator\">\n    <div class=\"h4 text-center\">\n      {{ 'Select widget' | translate }}\n    </div>\n  </div>\n  <div class=\"card-inner-scroll fit-h bg-level-2\">\n    <div\n      class=\"bg-level-0 p-l-24 p-r-24 p-t-8 p-b-8 sticky-header-top-0 elevation-md\"\n      style=\"z-index: 2\"\n    >\n      <div class=\"row\">\n        <div class=\"col-sm-6 col-sm-offset-3\">\n          <div class=\"input-group input-group-search\">\n            <input\n              class=\"form-control\"\n              [attr.aria-label]=\"'Search' | translate\"\n              placeholder=\"{{ 'Search\u2026' | translate }}\"\n              type=\"text\"\n              data-cy=\"widget-config--Search\"\n              [(ngModel)]=\"searchTerm\"\n              [ngModelOptions]=\"{ standalone: true }\"\n              (keydown)=\"searchChange$.next($event)\"\n            />\n            <span class=\"input-group-btn\">\n              <button\n                class=\"btn btn-dot\"\n                title=\"{{ 'Search' | translate }}\"\n                type=\"button\"\n                (click)=\"resetSearch()\"\n              >\n                <i [c8yIcon]=\"searchTerm.length === 0 ? 'search' : 'close'\"></i>\n              </button>\n            </span>\n          </div>\n        </div>\n      </div>\n    </div>\n    <div class=\"card-block\">\n      <div class=\"card-group p-l-24 p-r-24 d-grid grid__col--auto-300 gap-24 card-select m-b-0\">\n        <button\n          class=\"btn-clean d-col card m-b-0\"\n          [title]=\"cmp.description || cmp.label | translate\"\n          type=\"button\"\n          data-cy=\"widget-config--widget-list\"\n          *ngFor=\"let cmp of searchResult || components\"\n          (click)=\"select(cmp)\"\n        >\n          <div\n            class=\"border-bottom\"\n            role=\"presentation\"\n          >\n            <ng-container *ngIf=\"!cmp.previewImage; else previewImage\">\n              <div class=\"h1\"><i c8yIcon=\"file-image-o\"></i></div>\n              <small translate>Preview not available</small>\n            </ng-container>\n            <ng-template #previewImage>\n              <img\n                class=\"widget-thumbnail\"\n                alt=\"{{ cmp.label | translate }}\"\n                [src]=\"cmp.previewImage\"\n              />\n            </ng-template>\n          </div>\n          <div class=\"card-block\">\n            <p class=\"card-title text-truncate text-medium\">\n              <c8y-highlight\n                text=\"{{ cmp.label | translate }}\"\n                [pattern]=\"searchTerm\"\n              ></c8y-highlight>\n            </p>\n            <p\n              class=\"small text-default\"\n              style=\"white-space: wrap\"\n            >\n              {{ cmp.description | translate }}\n            </p>\n          </div>\n        </button>\n        <c8y-ui-empty-state\n          class=\"p-24 grid__col--fullspan\"\n          [icon]=\"'search'\"\n          [title]=\"'No widgets found.' | translate\"\n          [subtitle]=\"' Rephrase your search term.' | translate\"\n          *ngIf=\"searchResult && searchResult.length === 0\"\n        >\n          <button\n            class=\"btn btn-primary\"\n            title=\"{{ 'Reset search' | translate }}\"\n            type=\"button\"\n            (click)=\"resetSearch()\"\n          >\n            {{ 'Reset search' | translate }}\n          </button>\n        </c8y-ui-empty-state>\n      </div>\n    </div>\n  </div>\n  <div class=\"card-footer text-center separator flex-no-shrink\">\n    <button\n      class=\"btn btn-default\"\n      title=\"{{ 'Cancel' | translate }}\"\n      type=\"button\"\n      data-cy=\"widget-config--cancel-widget\"\n      (click)=\"close()\"\n    >\n      {{ 'Cancel' | translate }}\n    </button>\n  </div>\n</ng-container>\n\n<!-- widget configuration -->\n<ng-container *ngIf=\"!!(widgetConfigService.selected$ | async)\">\n  <div class=\"card-header d-block separator-bottom flex-no-shrink\">\n    <div\n      class=\"h3 p-t-16\"\n      title=\"{{ selected?.label | translate }}\"\n    >\n      <span>{{ selected?.label | translate }}</span>\n      <button\n        class=\"btn btn-default btn-sm m-l-8\"\n        [title]=\"'Change widget' | translate\"\n        (click)=\"backToWidgetSelection(); (false)\"\n      >\n        <i c8yIcon=\"replace\"></i>\n        {{ 'Change widget' | translate }}\n      </button>\n    </div>\n    <div\n      class=\"p-t-8\"\n      *ngIf=\"selected\"\n    >\n      <p>\n        {{ selected.description | translate }}\n      </p>\n    </div>\n  </div>\n\n  <div class=\"d-grid grid__col--560-auto min-height-0 flex-grow\">\n    <div class=\"bg-level-1 inner-scroll\">\n      <div class=\"p-16 flex-no-shrink separator-bottom bg-level-1\">\n        <c8y-form-group>\n          <label\n            for=\"widgetTitle\"\n            translate\n          >\n            Widget title\n          </label>\n          <input\n            class=\"form-control\"\n            id=\"widgetTitle\"\n            placeholder=\"{{ 'e.g.' | translate }} {{ componentLabel | translate }}\"\n            name=\"title\"\n            type=\"text\"\n            required\n            [(ngModel)]=\"selected?.data.title\"\n          />\n        </c8y-form-group>\n      </div>\n\n      <c8y-ui-empty-state\n        class=\"p-24\"\n        [icon]=\"'settings'\"\n        [title]=\"'No configuration needed.' | translate\"\n        [subtitle]=\"'This widget does not need any specific configuration.' | translate\"\n        [horizontal]=\"true\"\n        *ngIf=\"!(widgetConfigService.hasConfig$ | async)\"\n      ></c8y-ui-empty-state>\n      <div>\n        <form\n          name=\"form\"\n          #configForm=\"ngForm\"\n        >\n          <ng-container *ngIf=\"widgetConfigService.hasConfig$ | async\">\n            <ng-container *ngIf=\"widgetConfigService.providers$ | async as providers\">\n              <ng-container\n                *c8yComponentOutlet=\"widgetConfigRoot; providers: providers\"\n              ></ng-container>\n            </ng-container>\n          </ng-container>\n        </form>\n      </div>\n    </div>\n    <div class=\"inner-scroll p-32 p-t-0\">\n      <c8y-widget-preview [previewClasses]=\"getStyle(true)\"></c8y-widget-preview>\n\n      <c8y-appearance-settings\n        [(themeClass)]=\"styling.contentClass\"\n        [(headerClass)]=\"styling.headerClass\"\n        [possibleStylingTheme]=\"possibleStyling.WIDGET_CONTENT_CLASSES\"\n        [possibleStylingHeader]=\"possibleStyling.WIDGET_HEADER_CLASSES\"\n        [defaultThemeClass]=\"defaultStyling.contentClass\"\n        [defaultHeaderClass]=\"defaultStyling.headerClass\"\n        [columns]=\"2\"\n      ></c8y-appearance-settings>\n    </div>\n  </div>\n\n  <div class=\"card-footer separator text-center\">\n    <button\n      class=\"btn btn-default\"\n      title=\"{{ 'Cancel' | translate }}\"\n      type=\"button\"\n      data-cy=\"widget-config--cancel-widget\"\n      (click)=\"close()\"\n    >\n      {{ 'Cancel' | translate }}\n    </button>\n    <button\n      class=\"btn btn-primary\"\n      title=\"{{ 'Save' | translate }}\"\n      type=\"submit\"\n      data-cy=\"widget-config--save-widget\"\n      (click)=\"save()\"\n      [disabled]=\"(contextDashboardService.formDisabled$ | async) || isSaveDisabled()\"\n      c8yProductExperience\n      [actionName]=\"current ? 'editWidget' : 'createWidget'\"\n      [actionData]=\"{ widgetName: selected && selected.id }\"\n    >\n      {{ 'Save' | translate }}\n    </button>\n  </div>\n</ng-container>\n", dependencies: [{ kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i5.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i5.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i5.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i5.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i5.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i5.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i5.NgForm, selector: "form:not([ngNoForm]):not([formGroup]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "directive", type: NgFor, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: C8yTranslateDirective, selector: "[translate],[ngx-translate]" }, { kind: "component", type: HighlightComponent, selector: "c8y-highlight", inputs: ["pattern", "text", "elementClass", "shouldTrimPattern"] }, { kind: "component", type: EmptyStateComponent, selector: "c8y-ui-empty-state", inputs: ["icon", "title", "subtitle", "horizontal"] }, { kind: "component", type: FormGroupComponent, selector: "c8y-form-group", inputs: ["hasError", "hasWarning", "hasSuccess", "novalidation", "status"] }, { kind: "directive", type: RequiredInputPlaceholderDirective, selector: "input[required], input[formControlName]" }, { kind: "directive", type: C8yComponentOutlet, selector: "[c8yComponentOutlet]", inputs: ["c8yComponentOutlet", "c8yComponentOutletInjector", "c8yComponentOutletEnvironmentInjector", "c8yComponentOutletProviders", "c8yComponentOutletInitialState"] }, { kind: "component", type: WidgetPreviewComponent, selector: "c8y-widget-preview", inputs: ["previewClasses"] }, { kind: "component", type: AppearanceSettingsComponent, selector: "c8y-appearance-settings", inputs: ["themeClass", "headerClass", "defaultThemeClass", "defaultHeaderClass", "dashboardSettings", "possibleStylingTheme", "possibleStylingHeader", "columns"], outputs: ["themeClassChange", "headerClassChange", "onChange"] }, { kind: "directive", type: ProductExperienceDirective, selector: "[c8yProductExperience]", inputs: ["actionName", "actionData", "inherit", "suppressDataOverriding"] }, { kind: "pipe", type: C8yTranslatePipe, name: "translate" }, { kind: "pipe", type: AsyncPipe, name: "async" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: WidgetConfigComponent, decorators: [{
            type: Component,
            args: [{ selector: 'c8y-widget-config', host: { class: 'd-contents' }, imports: [
                        NgIf,
                        FormsModule,
                        IconDirective,
                        NgFor,
                        C8yTranslateDirective,
                        HighlightComponent,
                        EmptyStateComponent,
                        FormGroupComponent,
                        RequiredInputPlaceholderDirective,
                        C8yComponentOutlet,
                        WidgetPreviewComponent,
                        AppearanceSettingsComponent,
                        ProductExperienceDirective,
                        C8yTranslatePipe,
                        AsyncPipe
                    ], template: "<!-- select widget -->\n<ng-container *ngIf=\"!(widgetConfigService.selected$ | async)\">\n  <div class=\"card-header j-c-center separator\">\n    <div class=\"h4 text-center\">\n      {{ 'Select widget' | translate }}\n    </div>\n  </div>\n  <div class=\"card-inner-scroll fit-h bg-level-2\">\n    <div\n      class=\"bg-level-0 p-l-24 p-r-24 p-t-8 p-b-8 sticky-header-top-0 elevation-md\"\n      style=\"z-index: 2\"\n    >\n      <div class=\"row\">\n        <div class=\"col-sm-6 col-sm-offset-3\">\n          <div class=\"input-group input-group-search\">\n            <input\n              class=\"form-control\"\n              [attr.aria-label]=\"'Search' | translate\"\n              placeholder=\"{{ 'Search\u2026' | translate }}\"\n              type=\"text\"\n              data-cy=\"widget-config--Search\"\n              [(ngModel)]=\"searchTerm\"\n              [ngModelOptions]=\"{ standalone: true }\"\n              (keydown)=\"searchChange$.next($event)\"\n            />\n            <span class=\"input-group-btn\">\n              <button\n                class=\"btn btn-dot\"\n                title=\"{{ 'Search' | translate }}\"\n                type=\"button\"\n                (click)=\"resetSearch()\"\n              >\n                <i [c8yIcon]=\"searchTerm.length === 0 ? 'search' : 'close'\"></i>\n              </button>\n            </span>\n          </div>\n        </div>\n      </div>\n    </div>\n    <div class=\"card-block\">\n      <div class=\"card-group p-l-24 p-r-24 d-grid grid__col--auto-300 gap-24 card-select m-b-0\">\n        <button\n          class=\"btn-clean d-col card m-b-0\"\n          [title]=\"cmp.description || cmp.label | translate\"\n          type=\"button\"\n          data-cy=\"widget-config--widget-list\"\n          *ngFor=\"let cmp of searchResult || components\"\n          (click)=\"select(cmp)\"\n        >\n          <div\n            class=\"border-bottom\"\n            role=\"presentation\"\n          >\n            <ng-container *ngIf=\"!cmp.previewImage; else previewImage\">\n              <div class=\"h1\"><i c8yIcon=\"file-image-o\"></i></div>\n              <small translate>Preview not available</small>\n            </ng-container>\n            <ng-template #previewImage>\n              <img\n                class=\"widget-thumbnail\"\n                alt=\"{{ cmp.label | translate }}\"\n                [src]=\"cmp.previewImage\"\n              />\n            </ng-template>\n          </div>\n          <div class=\"card-block\">\n            <p class=\"card-title text-truncate text-medium\">\n              <c8y-highlight\n                text=\"{{ cmp.label | translate }}\"\n                [pattern]=\"searchTerm\"\n              ></c8y-highlight>\n            </p>\n            <p\n              class=\"small text-default\"\n              style=\"white-space: wrap\"\n            >\n              {{ cmp.description | translate }}\n            </p>\n          </div>\n        </button>\n        <c8y-ui-empty-state\n          class=\"p-24 grid__col--fullspan\"\n          [icon]=\"'search'\"\n          [title]=\"'No widgets found.' | translate\"\n          [subtitle]=\"' Rephrase your search term.' | translate\"\n          *ngIf=\"searchResult && searchResult.length === 0\"\n        >\n          <button\n            class=\"btn btn-primary\"\n            title=\"{{ 'Reset search' | translate }}\"\n            type=\"button\"\n            (click)=\"resetSearch()\"\n          >\n            {{ 'Reset search' | translate }}\n          </button>\n        </c8y-ui-empty-state>\n      </div>\n    </div>\n  </div>\n  <div class=\"card-footer text-center separator flex-no-shrink\">\n    <button\n      class=\"btn btn-default\"\n      title=\"{{ 'Cancel' | translate }}\"\n      type=\"button\"\n      data-cy=\"widget-config--cancel-widget\"\n      (click)=\"close()\"\n    >\n      {{ 'Cancel' | translate }}\n    </button>\n  </div>\n</ng-container>\n\n<!-- widget configuration -->\n<ng-container *ngIf=\"!!(widgetConfigService.selected$ | async)\">\n  <div class=\"card-header d-block separator-bottom flex-no-shrink\">\n    <div\n      class=\"h3 p-t-16\"\n      title=\"{{ selected?.label | translate }}\"\n    >\n      <span>{{ selected?.label | translate }}</span>\n      <button\n        class=\"btn btn-default btn-sm m-l-8\"\n        [title]=\"'Change widget' | translate\"\n        (click)=\"backToWidgetSelection(); (false)\"\n      >\n        <i c8yIcon=\"replace\"></i>\n        {{ 'Change widget' | translate }}\n      </button>\n    </div>\n    <div\n      class=\"p-t-8\"\n      *ngIf=\"selected\"\n    >\n      <p>\n        {{ selected.description | translate }}\n      </p>\n    </div>\n  </div>\n\n  <div class=\"d-grid grid__col--560-auto min-height-0 flex-grow\">\n    <div class=\"bg-level-1 inner-scroll\">\n      <div class=\"p-16 flex-no-shrink separator-bottom bg-level-1\">\n        <c8y-form-group>\n          <label\n            for=\"widgetTitle\"\n            translate\n          >\n            Widget title\n          </label>\n          <input\n            class=\"form-control\"\n            id=\"widgetTitle\"\n            placeholder=\"{{ 'e.g.' | translate }} {{ componentLabel | translate }}\"\n            name=\"title\"\n            type=\"text\"\n            required\n            [(ngModel)]=\"selected?.data.title\"\n          />\n        </c8y-form-group>\n      </div>\n\n      <c8y-ui-empty-state\n        class=\"p-24\"\n        [icon]=\"'settings'\"\n        [title]=\"'No configuration needed.' | translate\"\n        [subtitle]=\"'This widget does not need any specific configuration.' | translate\"\n        [horizontal]=\"true\"\n        *ngIf=\"!(widgetConfigService.hasConfig$ | async)\"\n      ></c8y-ui-empty-state>\n      <div>\n        <form\n          name=\"form\"\n          #configForm=\"ngForm\"\n        >\n          <ng-container *ngIf=\"widgetConfigService.hasConfig$ | async\">\n            <ng-container *ngIf=\"widgetConfigService.providers$ | async as providers\">\n              <ng-container\n                *c8yComponentOutlet=\"widgetConfigRoot; providers: providers\"\n              ></ng-container>\n            </ng-container>\n          </ng-container>\n        </form>\n      </div>\n    </div>\n    <div class=\"inner-scroll p-32 p-t-0\">\n      <c8y-widget-preview [previewClasses]=\"getStyle(true)\"></c8y-widget-preview>\n\n      <c8y-appearance-settings\n        [(themeClass)]=\"styling.contentClass\"\n        [(headerClass)]=\"styling.headerClass\"\n        [possibleStylingTheme]=\"possibleStyling.WIDGET_CONTENT_CLASSES\"\n        [possibleStylingHeader]=\"possibleStyling.WIDGET_HEADER_CLASSES\"\n        [defaultThemeClass]=\"defaultStyling.contentClass\"\n        [defaultHeaderClass]=\"defaultStyling.headerClass\"\n        [columns]=\"2\"\n      ></c8y-appearance-settings>\n    </div>\n  </div>\n\n  <div class=\"card-footer separator text-center\">\n    <button\n      class=\"btn btn-default\"\n      title=\"{{ 'Cancel' | translate }}\"\n      type=\"button\"\n      data-cy=\"widget-config--cancel-widget\"\n      (click)=\"close()\"\n    >\n      {{ 'Cancel' | translate }}\n    </button>\n    <button\n      class=\"btn btn-primary\"\n      title=\"{{ 'Save' | translate }}\"\n      type=\"submit\"\n      data-cy=\"widget-config--save-widget\"\n      (click)=\"save()\"\n      [disabled]=\"(contextDashboardService.formDisabled$ | async) || isSaveDisabled()\"\n      c8yProductExperience\n      [actionName]=\"current ? 'editWidget' : 'createWidget'\"\n      [actionData]=\"{ widgetName: selected && selected.id }\"\n    >\n      {{ 'Save' | translate }}\n    </button>\n  </div>\n</ng-container>\n" }]
        }], ctorParameters: () => [{ type: WidgetService }, { type: i2.BottomDrawerRef }, { type: ContextDashboardService }, { type: WidgetConfigService }], propDecorators: { configForm: [{
                type: ViewChild,
                args: ['configForm', { static: false }]
            }] } });

/**
 * The context dashboard is a dashboard which resolves it data from the current context (device or group)
 * it is displayed on. It usually uses the route.data for it, but you can pass
 * a different managedObject to the [mo] input parameter to change that behavior.
 */
class ContextDashboardComponent {
    get disabled() {
        return !this.hasPermissionToEditDashboard || !this.dashboardDetails?.isCollapsed;
    }
    constructor(route, router, contextDashboardService, alert, renderer, moduleConfig, widgetService, bottomDrawerService, gainsightService, actionBarService, translateService, modal, editModeService) {
        this.route = route;
        this.router = router;
        this.contextDashboardService = contextDashboardService;
        this.alert = alert;
        this.renderer = renderer;
        this.moduleConfig = moduleConfig;
        this.widgetService = widgetService;
        this.bottomDrawerService = bottomDrawerService;
        this.gainsightService = gainsightService;
        this.actionBarService = actionBarService;
        this.translateService = translateService;
        this.modal = modal;
        this.editModeService = editModeService;
        this.childrenClasses = '';
        this.setTitle = false;
        this.defaultWidgets = [];
        this._canCopy = true;
        this.canDelete = true;
        this.isLoading = true;
        this.showContextHelpButton = true;
        /**
         * Hides dashboard availability selection.
         */
        this.hideAvailability = false;
        this.class = '';
        this.widgets = [];
        this.canCopy = true;
        this.dashboardTypeLabel = gettext('Dashboard for "{{ dashboardType }}"');
        this.dashboardTypePopover = gettext(`
  The layout and configuration of this dashboard is used by all other assets of model type
  <strong>{{ dashboardType }}</strong>. Any changes made to this dashboard instance will be applied to all
  other instances.`);
        this.hasPermissionToEditDashboard = false;
        this.destroy$ = new Subject();
    }
    ngOnInit() {
        if (!this.name) {
            this.loadContextDashboard();
            return;
        }
        this.loadNamedDashboard();
    }
    async beforeUnloadHandler($event) {
        const canDeactivate = await this.canDeactivate(true);
        if (!canDeactivate) {
            $event.returnValue = true;
        }
    }
    /**
     * Applies the current context to the widget
     * @param widget The widget to apply the context to.
     */
    applyDeviceTarget(widget) {
        if (widget.config.device) {
            widget.config.device = { id: this.context.id, name: this.context.name };
        }
    }
    /**
     * Removes the route listener.
     */
    ngOnDestroy() {
        this.destroy$.next();
        this.destroy$.complete();
        if (this.dataSub) {
            this.dataSub.unsubscribe();
        }
    }
    /**
     * Guards component from unintended navigation away or closing tab without saving changes.
     * Navigation continues if true is returned, and navigation is cancelled if returned value is false.
     * @param omitConfirm Omits confirm calls inside method body (without this param, when method is used in
     * on 'beforeunload' event handler, error is thrown in console because confirm is blocked by browser anyway and returns false)
     */
    async canDeactivate(omitConfirm = false) {
        const canDeactivate = (await this.dashboardDetails.canDeactivate(omitConfirm)) &&
            (await this.widgetsDashboard.canDeactivate(omitConfirm));
        // Needed in order to reset edit mode, otherwise when switching between dashboards, the edit mode is still enabled.
        if (canDeactivate) {
            await this.cancelEditMode(true);
        }
        return canDeactivate;
    }
    /**
     * Restores the dashboard widgets to the default widgets.
     */
    async restore() {
        const mesg = gettext('You are about to reset the widgets of this dashboard. All changes to the dashboard widgets will get lost and cannot be recovered. Do you want to proceed?');
        try {
            await this.modal.confirm(gettext('Reset widgets'), mesg, Status.WARNING, {
                ok: gettext('Reset`dashboard`'),
                cancel: gettext('Cancel')
            });
            this.isLoading = true;
            this.mo.c8y_Dashboard.children = this.contextDashboardService.mapWidgets(this.defaultWidgets);
            this.mo.c8y_Dashboard.historyDescription = { changeType: 'reset' };
            await this.contextDashboardService.update(this.mo);
            await this.onLoad();
        }
        catch (error) {
            if (error) {
                this.alert.addServerFailure(error);
            }
        }
        finally {
            this.isLoading = false;
        }
    }
    /**
     * Method called on every widgets dimensions or position change but also for adding new widget (because adding
     * widget causes layout changes). It recognizes what type of change has been done and updates current widgets state
     * accordingly.
     * @param child Change object.
     */
    positionOrWidgetDimensionChange(child) {
        const currentState = this.editModeService.getCurrentState()?.children || {};
        const descriptionProp = child.children.length === Object.values(currentState).length ? 'arrangement' : 'added';
        let changeName;
        let widgetsChanged;
        if (descriptionProp === 'added') {
            changeName = DASHBOARD_CHILDREN_STATE_NAME.added;
            const addedWidget = child.children.find(c => !currentState[c.data.id]);
            widgetsChanged = [addedWidget.data];
        }
        else {
            changeName = DASHBOARD_CHILDREN_STATE_NAME.arrangement;
            widgetsChanged = child.children
                .map(c => {
                const lastStateChild = currentState[c.data.id];
                if (c.x != lastStateChild._x ||
                    c.y != lastStateChild._y ||
                    c.width != lastStateChild._width ||
                    c.height != lastStateChild._height) {
                    return c.data;
                }
            })
                .filter(Boolean);
        }
        this.updateDashboardChildren(child, changeName, descriptionProp, widgetsChanged);
    }
    async revertChange(revertType) {
        let dashboardChildren;
        if (revertType === 'undo') {
            dashboardChildren = this.editModeService.undo().children;
        }
        else {
            dashboardChildren = this.editModeService.redo().children;
        }
        await this.setWidgets(dashboardChildren);
    }
    /**
     * Updates all dashboards children's. Useful for position changes on the dashboard.
     * @param child The child to change.
     * @param changeName Name of the change to indicate it on undo/redo button.
     * @param descriptionProp Property to add to dashboard change history.
     * @param widgetsChanged List of changed widgets.
     */
    updateDashboardChildren(child, changeName, descriptionProp, widgetsChanged) {
        const { children } = child;
        const mappedChildren = keyBy(children.map(c => this.componentToWidget(c)), 'id');
        this.setNewState({ name: changeName, children: mappedChildren }, descriptionProp, widgetsChanged);
    }
    /**
     * Copies the dashboard and current context to a clipboard.
     */
    async copyDashboard() {
        const viewContext = this.route.parent.snapshot.data?.context;
        this.contextDashboardService.copyClipboard = {
            dashboardId: this.mo.id,
            dashboard: cloneDeep(this.mo.c8y_Dashboard),
            context: cloneDeep({ context: viewContext, contextData: this.context })
        };
        if (viewContext) {
            const ctx = viewContext.split('/').shift();
            const msg = this.translateService.instant('Dashboard copied. Navigate to the desired {{ ctx }} and select "Paste dashboard"', { ctx });
            this.alert.success(msg);
        }
        this.actionBarService.refresh();
    }
    editDashboard() {
        const isEdit = !!this.mo.id;
        this.dashboardDetails.show(isEdit);
    }
    /**
     * Remove the complete dashboard and navigate away.
     */
    async deleteDashboard() {
        await this.contextDashboardService.delete(this.mo);
        if (this.route.parent) {
            const route = this.route.parent.snapshot.url.map(segment => segment.path).join('/');
            this.router.navigateByUrl(route);
        }
        await this.onDeleteGSEvent();
    }
    get isDeviceTypeDashboard() {
        return !!this.dashboard?.deviceType;
    }
    /**
     * Edits a widget on the dashboard.
     * @param change The widget change event.
     */
    async editWidget(change) {
        const { x, y, width, height } = change.source;
        const component = await this.widgetService.getWidgetDefinition(change.widget.name || change.widget.componentId);
        if (!component) {
            this.addWidget();
            return;
        }
        await this.addWidget({
            ...component,
            data: { ...component.data, ...change.widget, _x: x, _y: y, _width: width, _height: height }
        });
    }
    /**
     * Adds a widget to the dashboard.
     * @param selected Define a selected component to switch to edit mode directly.
     */
    async addWidget(selected) {
        const partialCloneSelected = selected
            ? { ...selected, data: cloneDeep(selected.data) }
            : selected;
        const activeContext = this.context.contextData ? this.context.contextData : this.context;
        const initialState = {
            mo: this.mo,
            context: this.context.c8y_Report ? {} : activeContext,
            selected: partialCloneSelected
        };
        const bottomDrawer = this.bottomDrawerService.openDrawer(WidgetConfigComponent, {
            initialState,
            closeOnNavigation: false,
            disableClickOutside: true
        });
        try {
            const newWidget = await bottomDrawer.instance.result;
            if (!this.mo.c8y_Dashboard.children) {
                this.mo.c8y_Dashboard.children = {};
            }
            newWidget.classes = this.mergeWidgetClasses(newWidget);
            await this.updateWidget(newWidget);
            // New state in edit mode is added only for widget update. When adding widget, layout change is always triggered,
            // so it's easier to set new state on layout change only
            if (selected) {
                const children = {
                    ...this.editModeService.getCurrentState().children,
                    [newWidget.id]: newWidget
                };
                this.setNewState({ name: DASHBOARD_CHILDREN_STATE_NAME.config, children }, 'config', [
                    newWidget
                ]);
            }
        }
        catch (ex) {
            // intended empty
        }
    }
    async saveWidgetsToDashboard() {
        const currentState = this.editModeService.getCurrentState();
        const editedChildren = currentState.children;
        // first-time save (no `id` yet): create the dashboard, then immediately update it with the edited widgets
        if (!this.mo.id) {
            this.mo = await this.contextDashboardService.create(this.mo.c8y_Dashboard, this.context, this.name);
        }
        this.mo.c8y_Dashboard.children = editedChildren;
        this.mo.c8y_Dashboard.historyDescription = {
            changeType: 'update',
            widgetChanges: this.mapStateToHistoryDescription(currentState.changeHistory)
        };
        await this.contextDashboardService.update(this.mo);
        this.editModeService.reset();
        this.editModeService.init({
            name: DASHBOARD_CHILDREN_STATE_NAME.initial,
            children: editedChildren,
            changeHistory: {}
        });
        this.isCopyDisabled = this.getDashboardCopyPermissionState();
    }
    async cancelEditMode(onDeactivate = false) {
        const dashboardChildren = this.editModeService.reset();
        if (this.dashboard) {
            this.dashboard.historyDescription = {};
        }
        this.widgetsDashboard.editMode$.next(false);
        if (!onDeactivate) {
            // when setWidgets is called during navigation from device to device, navigation fails
            await this.setWidgets(dashboardChildren.children);
        }
    }
    /**
     * Updates a widget or adds a new one if it doesn't exist on
     * the dashboard.
     * @param widget The new widget
     */
    async updateWidget(widget) {
        const index = findIndex(this.widgets, { id: widget.id });
        const isNew = index === -1;
        const mappedWidget = await this.widgetService.mapLegacy(widget);
        if (isNew) {
            this.widgets.push(mappedWidget);
        }
        else {
            this.widgets.splice(index, 1, mappedWidget);
        }
    }
    /**
     * Removes a widget and rearranges the remaining ones
     * if necessary.
     * @param change The change event.
     */
    async deleteWidget(change) {
        try {
            const { widget, source } = change;
            const removed = this.widgets.find(({ id }) => id === widget.id);
            this.widgets.splice(this.widgets.indexOf(removed), 1);
            const { dashboard } = source;
            dashboard.children = dashboard.children.filter(c => c.data.id !== widget.id);
            // using setTimeout to give the component the chance to remove it.
            const sleep = timeout => new Promise(resolve => setTimeout(resolve, timeout));
            await sleep(0);
            const child = new DashboardChildChange(source);
            child.collapseUpAll();
            this.updateDashboardChildren(child, DASHBOARD_CHILDREN_STATE_NAME.removed, 'removed', [
                widget
            ]);
        }
        catch (e) {
            this.alert.addServerFailure(e);
        }
    }
    /**
     * This is a workaround to ensure that the dragged-element
     * (which is attached to the body) has the right styling.
     */
    addDashboardClassToBody() {
        this.class.split(' ').forEach(cssClass => {
            this.renderer.addClass(document.body, cssClass);
        });
    }
    /**
     * This is a workaround to ensure that the dragged-element
     * (which is attached to the body) has the right styling.
     */
    removeDashboardClassFromBody() {
        this.class.split(' ').forEach(cssClass => {
            this.renderer.removeClass(document.body, cssClass);
        });
    }
    async onLoad(trackExperience) {
        this.isNamedDashboard = this.contextDashboardService.isNamed(this.mo);
        this.hasPermissionToEditDashboard = this.dashboard
            ? await this.contextDashboardService.canEditDashboard(this.mo)
            : false;
        this.canCopy =
            this._canCopy &&
                (this.contextDashboardService.isDeviceDashboard(this.mo) ||
                    this.contextDashboardService.isDeviceType(this.mo) ||
                    this.contextDashboardService.isGroupDashboard(this.mo));
        const dashboardChildren = cloneDeep(this.mo.c8y_Dashboard?.children);
        const dashboardClasses = {
            'c8y-grid-dashboard': true,
            dashboard: true,
            ...this.dashboard?.classes
        };
        this.isCopyDisabled = this.dashboard
            ? this.getDashboardCopyPermissionState()
            : { state: false, reason: null };
        this.editModeService.init({
            name: gettext('Initial state'),
            children: dashboardChildren,
            changeHistory: {}
        });
        await this.setWidgets(dashboardChildren, trackExperience);
        this.class = Object.keys(dashboardClasses).join(' ');
        if (this.isReport) {
            this.addReportDashboardSettings();
        }
        this.isLoading = false;
        if (!this.dashboard) {
            this.dashboardDetails.show(false);
        }
        else {
            await this.onLoadGSEvent();
        }
    }
    async dashboardPreview(layoutChanges) {
        if (layoutChanges?.classes) {
            const dashboardClasses = {
                'c8y-grid-dashboard': true,
                dashboard: true,
                ...layoutChanges.classes
            };
            this.class = Object.keys(dashboardClasses).join(' ');
        }
        else if (this.dashboard) {
            this.dashboard.widgetClasses = {
                ...this.dashboard.widgetClasses,
                ...layoutChanges.widgetClasses
            };
        }
    }
    async reloadDashboard(dashboardMo) {
        this.dashboard = dashboardMo.c8y_Dashboard;
        this.mo = dashboardMo;
        await this.onLoad();
    }
    async setWidgets(dashboardChildren, trackExperience = false) {
        const isDeviceType = this.contextDashboardService.isDeviceType(this.mo);
        this.widgets = await Promise.all(Object.values(dashboardChildren || {}).map(widget => {
            widget.classes = this.mergeWidgetClasses(widget);
            if (isDeviceType) {
                this.applyDeviceTarget(widget);
            }
            if (trackExperience) {
                this.gainsightService.triggerEvent('loadWidget', {
                    widgetName: widget.componentId || widget.name
                });
            }
            return this.widgetService.mapLegacy(widget);
        }));
    }
    loadContextDashboard() {
        this.dataSub = this.route.data.subscribe(({ dashboard, isReport }) => {
            this.context = this.route.parent.snapshot.data.contextData || {};
            this.title = this.context?.name;
            this.mo = dashboard;
            this.dashboard = this.mo.c8y_Dashboard;
            this.isReport = isReport || this.contextDashboardService.isReport(this.mo);
            if (this.isReport && this.dashboard && !this.dashboard?.name) {
                this.dashboard.name = this.title;
            }
            if (this.isReport && this.dashboard) {
                this.dashboard.c8y_IsNavigatorNode = this.context.c8y_IsNavigatorNode;
            }
            this.patchSensorPhoneDashboard(this.dashboard, this.mo.owner, this.context?.type);
            this.onLoad(true);
        });
    }
    /**
     * To enable translation for widgets within the dashboard, the "translateWidgetTitle" flag must be enabled.
     * The property needs to be patched, as the "Sensor App" does not provide this setting.
     */
    patchSensorPhoneDashboard(dashboard, owner, type) {
        if (type === 'c8y_SensorPhone' && owner?.includes('device_phone')) {
            dashboard.translateWidgetTitle = true;
        }
    }
    loadNamedDashboard() {
        this.dataSub = this.contextDashboardService
            .getDashboard(this.name, this.defaultWidgets)
            .subscribe(mo => {
            this.context = this.context || {};
            this.mo = mo;
            this.dashboard = this.mo.c8y_Dashboard;
            this.onLoad(true);
        });
    }
    mergeWidgetClasses(widget) {
        const hasHeaderClass = WIDGET_HEADER_CLASSES.find(el => widget.classes && widget.classes[el.class]);
        const widgetClasses = hasHeaderClass
            ? { ...widget.classes }
            : { ...this.dashboard.widgetClasses, ...widget.classes };
        return {
            card: true,
            'card-dashboard': true,
            [kebabCase(widget.componentId || widget.name)]: true,
            ...widgetClasses
        };
    }
    componentToWidget(child) {
        return {
            ...omit(child.data, ['componentTransformConfigWithContext', 'transformConfigWithContext']), // remove legacy
            ...{ _x: child.x, _y: child.y, _width: child.width, _height: child.height }
        };
    }
    addReportDashboardSettings() {
        this.setTitle = true;
        this.title = this.context?.name || gettext('New report');
        this.breadcrumbSettings = { icon: 'th', label: 'Reports', path: 'reports' };
        this.canDelete = false;
    }
    getDashboardCopyPermissionState() {
        if (!this.contextDashboardService.hasPermissionsToCopyDashboard()) {
            return { state: false, reason: CopyDashboardDisabledReason.PERMISSIONS };
        }
        const allDashboardChildrenAreValid = every(this.mo.c8y_Dashboard.children, child => {
            const config = child.config || {};
            const dataPoints = config.datapoints || [];
            return !((config.device && config.device.id !== this.context.id) ||
                some(dataPoints, dataPoint => dataPoint.__target?.id !== this.context.id));
        });
        if (!allDashboardChildrenAreValid) {
            return { state: false, reason: CopyDashboardDisabledReason.WRONG_REFERENCE };
        }
        return { state: true };
    }
    setNewState(state, descriptionProp, widgetsChanged) {
        const description = this.getDescriptionForNewState(descriptionProp, widgetsChanged);
        this.editModeService.newState({ ...state, changeHistory: description });
    }
    getDescriptionForNewState(descriptionProp, widgetsChanged) {
        const changeHistory = this.editModeService.getCurrentState().changeHistory;
        if (!changeHistory[descriptionProp]) {
            changeHistory[descriptionProp] = {};
        }
        widgetsChanged.forEach(w => {
            if (descriptionProp === 'removed' && changeHistory.added?.[w.id]) {
                // if widget was added during current edit session and then removed, it should not be at description at all
                delete changeHistory.added[w.id];
                if (changeHistory.config?.[w.id]) {
                    delete changeHistory.config[w.id];
                }
                if (changeHistory.arrangement?.[w.id]) {
                    delete changeHistory?.arrangement[w.id];
                }
            }
            else if ((descriptionProp === 'config' || descriptionProp === 'arrangement') &&
                changeHistory.added?.[w.id]) {
                // if widget was added and then modified in current edit session, it should only be indicated as added
            }
            else {
                changeHistory[descriptionProp][w.id] = w;
            }
        });
        return changeHistory;
    }
    mapStateToHistoryDescription(description) {
        const historyDescription = {};
        for (const [key, value] of Object.entries(description)) {
            const widgets = Object.values(value);
            if (!widgets.length) {
                continue;
            }
            historyDescription[key] = Object.values(value).map(widget => widget.title);
        }
        return historyDescription;
    }
    async onDeleteGSEvent() {
        const parentName = await this.convertStringToHash(this.context?.name);
        const dashboardName = await this.convertStringToHash(this.dashboard?.name);
        this.gainsightService.triggerEvent(PRODUCT_EXPERIENCE.DASHBOARD.EVENTS.DASHBOARDS, {
            component: PRODUCT_EXPERIENCE.DASHBOARD.COMPONENTS.DELETE_DASHBOARD,
            action: PRODUCT_EXPERIENCE.DASHBOARD.ACTIONS.DELETE,
            name: dashboardName,
            id: this.mo.id,
            nameId: `${dashboardName}_${this.mo.id}`,
            isDashboardTemplate: this.mo.c8y_Dashboard.deviceType,
            parentAssetId: this.context?.id ? this.context.id : 'noContext',
            parentAssetName: this.context?.id ? parentName : 'noContext',
            parentAssetType: this.context?.id ? this.context.type : 'noContext',
            parentAssetNameId: this.context?.id ? `${parentName}_${this.context.id}` : 'noContext',
            parentAssetNameDashboardName: this.context?.id
                ? `${parentName}_${dashboardName}`
                : 'noContext',
            parentAssetIdDashboardId: this.context?.id ? `${this.context.id}_${this.mo.id}` : 'noContext',
            parentAssetNameDashboardId: this.context?.id ? `${parentName}_${this.mo.id}` : 'noContext',
            parentAssetNameIdDashboardNameId: this.context?.id
                ? `${parentName}_${this.context.id}_${dashboardName}_${this.mo.id}`
                : 'noContext',
            dashboardType: this.dashboard.deviceType ? this.dashboard.deviceTypeValue : null,
            context: this.contextDashboardService.getContextForGS(this.context)
        });
    }
    async onLoadGSEvent() {
        const parentName = await this.convertStringToHash(this.context?.name);
        const dashboardName = (await this.convertStringToHash(this.dashboard?.name)) ??
            this.extractDefaultDashboardName(this.mo) ??
            'none';
        this.gainsightService.triggerEvent(this.isReport
            ? PRODUCT_EXPERIENCE.DASHBOARD.EVENTS.REPORTS
            : PRODUCT_EXPERIENCE.DASHBOARD.EVENTS.DASHBOARDS, {
            component: PRODUCT_EXPERIENCE.DASHBOARD.COMPONENTS.DASHBOARD_VIEW,
            action: PRODUCT_EXPERIENCE.DASHBOARD.ACTIONS.LOAD,
            name: dashboardName,
            id: this.mo.id,
            isDashboardTemplate: this.mo.c8y_Dashboard.deviceType,
            nameId: `${dashboardName}_${this.mo.id}`,
            parentAssetId: this.context?.id ? this.context.id : 'noContext',
            parentAssetName: this.context?.id ? parentName : 'noContext',
            parentAssetType: this.context?.id ? this.context.type : 'noContext',
            parentAssetNameId: this.context?.id ? `${parentName}_${this.context.id}` : 'noContext',
            parentAssetNameDashboardName: this.context?.id
                ? `${parentName}_${dashboardName}`
                : 'noContext',
            parentAssetIdDashboardId: this.context?.id
                ? `${this.context.id}_${this.mo.id}`
                : 'noContext',
            parentAssetNameDashboardId: this.context?.id ? `${parentName}_${this.mo.id}` : 'noContext',
            parentAssetNameIdDashboardNameId: this.context?.id
                ? `${parentName}_${this.context.id}_${dashboardName}_${this.mo.id}`
                : 'noContext',
            dashboardType: this.dashboard.deviceType ? this.dashboard.deviceTypeValue : null,
            context: this.isReport
                ? PRODUCT_EXPERIENCE.DASHBOARD.CONTEXT.REPORT
                : this.contextDashboardService.getContextForGS(this.context)
        });
    }
    extractDefaultDashboardName(obj) {
        const nameKey = Object.keys(obj).find(key => key.startsWith('c8y_Dashboard!name!'));
        if (nameKey) {
            return nameKey.split('!').pop();
        }
        return null;
    }
    async convertStringToHash(str) {
        if (!str) {
            return null;
        }
        return (await this.gainsightService.shouldSendPiiData())
            ? str
            : this.gainsightService.hashGroupName(str);
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ContextDashboardComponent, deps: [{ token: i1.ActivatedRoute }, { token: i1.Router }, { token: ContextDashboardService }, { token: i2.AlertService }, { token: i0.Renderer2 }, { token: CONTEXT_DASHBOARD_CONFIG }, { token: WidgetService }, { token: i2.BottomDrawerService }, { token: i2.GainsightService }, { token: i2.ActionBarService }, { token: i2$1.TranslateService }, { token: i2.ModalService }, { token: DashboardEditModeService }], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.14", type: ContextDashboardComponent, isStandalone: true, selector: "c8y-context-dashboard", inputs: { name: "name", childrenClasses: "childrenClasses", context: "context", setTitle: "setTitle", defaultWidgets: "defaultWidgets", _canCopy: ["canCopy", "_canCopy"], canDelete: "canDelete", isLoading: "isLoading", breadcrumbSettings: "breadcrumbSettings", showContextHelpButton: "showContextHelpButton", translateWidgetTitle: "translateWidgetTitle", hideAvailability: "hideAvailability" }, host: { listeners: { "window:beforeunload": "beforeUnloadHandler($event)" }, properties: { "class": "this.class" }, styleAttribute: "\n      display: block;\n    ", classAttribute: "dashboard c8y-grid-dashboard" }, viewQueries: [{ propertyName: "dashboardDetails", first: true, predicate: DashboardDetailComponent, descendants: true, static: true }, { propertyName: "widgetsDashboard", first: true, predicate: WidgetsDashboardComponent, descendants: true, static: true }], ngImport: i0, template: "<c8y-title *ngIf=\"title\">\n  {{ title }}\n</c8y-title>\n\n<c8y-action-bar-item\n  [placement]=\"'right'\"\n  [priority]=\"10000\"\n  *ngIf=\"dashboard?.deviceType && dashboard.deviceTypeValue\"\n>\n  <button\n    class=\"btn-clean btn-link\"\n    popoverTitle=\"{{ 'Dashboard template' | translate }}\"\n    [attr.aria-label]=\"'Help' | translate\"\n    [popover]=\"dashboardTypePopoverRef\"\n    placement=\"bottom\"\n    triggers=\"focus\"\n    container=\"body\"\n    type=\"button\"\n  >\n    <span class=\"tag tag--info text-12\">\n      <span class=\"dashboard-template-marker\"></span>\n      {{ dashboardTypeLabel | translate: { dashboardType: dashboard.deviceTypeValue } }}\n    </span>\n  </button>\n  <ng-template #dashboardTypePopoverRef>\n    <div\n      [innerHTML]=\"dashboardTypePopover | translate: { dashboardType: dashboard.deviceTypeValue }\"\n    ></div>\n  </ng-template>\n</c8y-action-bar-item>\n\n<c8y-action-bar-item\n  [placement]=\"'right'\"\n  [priority]=\"-1\"\n  *ngIf=\"defaultWidgets.length > 0\"\n>\n  <button\n    class=\"btn btn-link\"\n    title=\"{{ 'Reset widgets' | translate }}\"\n    type=\"button\"\n    px-event=\"Reset dashboard\"\n    (click)=\"restore()\"\n    [disabled]=\"disabled || !mo?.id || (widgetsDashboard?.editMode$ | async)\"\n    data-cy=\"context-dashboard--button-reset-dashboard\"\n  >\n    <i\n      class=\"m-r-4\"\n      c8yIcon=\"reset\"\n    ></i>\n    <span class=\"visible-xs-inline hidden-sm visible-md-inline visible-lg-inline\">\n      {{ 'Reset widgets' | translate }}\n    </span>\n  </button>\n</c8y-action-bar-item>\n\n<c8y-help\n  src=\"/docs/cockpit/working-with-dashboards/#working-with-dashboards\"\n  *ngIf=\"showContextHelpButton\"\n></c8y-help>\n\n<c8y-dashboard-detail\n  class=\"d-contents\"\n  [isReport]=\"isReport\"\n  [dashboard]=\"dashboard\"\n  [mo]=\"mo\"\n  [context]=\"context\"\n  [deviceType]=\"context?.type\"\n  [hideAvailability]=\"hideAvailability\"\n  [isNamedDashboard]=\"isNamedDashboard\"\n  (dashboardSaved)=\"reloadDashboard($event)\"\n  (previewChanged)=\"dashboardPreview($event)\"\n></c8y-dashboard-detail>\n\n<c8y-widgets-dashboard\n  [context]=\"context\"\n  [contextDashboard]=\"dashboard\"\n  [widgets]=\"widgets\"\n  [isCopyDisabled]=\"isCopyDisabled\"\n  [settings]=\"{\n    isLoading: isLoading,\n    isDisabled: disabled,\n    canDelete: canDelete && !!dashboard,\n    translateWidgetTitle: dashboard?.translateWidgetTitle ?? translateWidgetTitle,\n    allowFullscreen: moduleConfig.allowFullscreen,\n    title: setTitle ? dashboard?.name || title : undefined,\n    widgetMargin: dashboard?.widgetMargin,\n    canCopy: canCopy && !!dashboard,\n    defaultWidth: dashboard?.columns >= 24 ? 8 : 4,\n    columns: dashboard?.columns || 12\n  }\"\n  [breadcrumb]=\"breadcrumbSettings\"\n  [editModeButtons]=\"{\n    undoButtonDisabled: editModeService.undoButtonDisabled,\n    changeToUndoName: editModeService.changeToUndoName,\n    redoButtonDisabled: !editModeService.redoStackLastItem,\n    changeToRedoName: editModeService.redoStackLastItem?.name\n  }\"\n  (onChangeDashboard)=\"positionOrWidgetDimensionChange($event)\"\n  (onAddWidget)=\"addWidget()\"\n  (onEditWidget)=\"editWidget($event)\"\n  (onDeleteWidget)=\"deleteWidget($event)\"\n  (onSaveDashboard)=\"saveWidgetsToDashboard()\"\n  (onCancelDashboard)=\"cancelEditMode()\"\n  (revertChange)=\"revertChange($event)\"\n  (onChangeStart)=\"addDashboardClassToBody()\"\n  (onChangeEnd)=\"removeDashboardClassFromBody()\"\n  (onEditDashboard)=\"editDashboard()\"\n  (onCopyDashboard)=\"copyDashboard()\"\n  (onDeleteDashboard)=\"deleteDashboard()\"\n></c8y-widgets-dashboard>\n", dependencies: [{ kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: TitleComponent, selector: "c8y-title", inputs: ["pageTitleUpdate"] }, { kind: "component", type: ActionBarItemComponent, selector: "c8y-action-bar-item", inputs: ["placement", "priority", "itemClass", "injector", "groupId", "inGroupPriority"] }, { kind: "directive", type: PopoverDirective, selector: "[popover]", inputs: ["adaptivePosition", "boundariesElement", "popover", "popoverContext", "popoverTitle", "placement", "outsideClick", "triggers", "container", "containerClass", "isOpen", "delay"], outputs: ["onShown", "onHidden"], exportAs: ["bs-popover"] }, { kind: "directive", type: IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "component", type: HelpComponent, selector: "c8y-help", inputs: ["src", "isCollapsed", "priority", "icon"] }, { kind: "component", type: DashboardDetailComponent, selector: "c8y-dashboard-detail", inputs: ["isReport", "deviceType", "context", "mo", "dashboard", "isNamedDashboard", "hideAvailability"], outputs: ["dashboardSaved", "previewChanged"] }, { kind: "component", type: WidgetsDashboardComponent, selector: "c8y-widgets-dashboard", inputs: ["widgets", "context", "contextDashboard", "settings", "isCopyDisabled", "breadcrumb", "editModeButtons"], outputs: ["onAddWidget", "onEditWidget", "onDeleteWidget", "onChangeDashboard", "onResize", "onEditDashboard", "onCopyDashboard", "onDeleteDashboard", "onChangeStart", "onChangeEnd", "onSaveDashboard", "onCancelDashboard", "revertChange"] }, { kind: "pipe", type: C8yTranslatePipe, name: "translate" }, { kind: "pipe", type: AsyncPipe, name: "async" }] }); }
}
__decorate([
    memoize(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [String]),
    __metadata("design:returntype", Promise)
], ContextDashboardComponent.prototype, "convertStringToHash", null);
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ContextDashboardComponent, decorators: [{
            type: Component,
            args: [{ selector: 'c8y-context-dashboard', host: {
                        style: `
      display: block;
    `,
                        class: 'dashboard c8y-grid-dashboard'
                    }, imports: [
                        NgIf,
                        TitleComponent,
                        ActionBarItemComponent,
                        PopoverDirective,
                        IconDirective,
                        HelpComponent,
                        DashboardDetailComponent,
                        WidgetsDashboardComponent,
                        C8yTranslatePipe,
                        AsyncPipe
                    ], template: "<c8y-title *ngIf=\"title\">\n  {{ title }}\n</c8y-title>\n\n<c8y-action-bar-item\n  [placement]=\"'right'\"\n  [priority]=\"10000\"\n  *ngIf=\"dashboard?.deviceType && dashboard.deviceTypeValue\"\n>\n  <button\n    class=\"btn-clean btn-link\"\n    popoverTitle=\"{{ 'Dashboard template' | translate }}\"\n    [attr.aria-label]=\"'Help' | translate\"\n    [popover]=\"dashboardTypePopoverRef\"\n    placement=\"bottom\"\n    triggers=\"focus\"\n    container=\"body\"\n    type=\"button\"\n  >\n    <span class=\"tag tag--info text-12\">\n      <span class=\"dashboard-template-marker\"></span>\n      {{ dashboardTypeLabel | translate: { dashboardType: dashboard.deviceTypeValue } }}\n    </span>\n  </button>\n  <ng-template #dashboardTypePopoverRef>\n    <div\n      [innerHTML]=\"dashboardTypePopover | translate: { dashboardType: dashboard.deviceTypeValue }\"\n    ></div>\n  </ng-template>\n</c8y-action-bar-item>\n\n<c8y-action-bar-item\n  [placement]=\"'right'\"\n  [priority]=\"-1\"\n  *ngIf=\"defaultWidgets.length > 0\"\n>\n  <button\n    class=\"btn btn-link\"\n    title=\"{{ 'Reset widgets' | translate }}\"\n    type=\"button\"\n    px-event=\"Reset dashboard\"\n    (click)=\"restore()\"\n    [disabled]=\"disabled || !mo?.id || (widgetsDashboard?.editMode$ | async)\"\n    data-cy=\"context-dashboard--button-reset-dashboard\"\n  >\n    <i\n      class=\"m-r-4\"\n      c8yIcon=\"reset\"\n    ></i>\n    <span class=\"visible-xs-inline hidden-sm visible-md-inline visible-lg-inline\">\n      {{ 'Reset widgets' | translate }}\n    </span>\n  </button>\n</c8y-action-bar-item>\n\n<c8y-help\n  src=\"/docs/cockpit/working-with-dashboards/#working-with-dashboards\"\n  *ngIf=\"showContextHelpButton\"\n></c8y-help>\n\n<c8y-dashboard-detail\n  class=\"d-contents\"\n  [isReport]=\"isReport\"\n  [dashboard]=\"dashboard\"\n  [mo]=\"mo\"\n  [context]=\"context\"\n  [deviceType]=\"context?.type\"\n  [hideAvailability]=\"hideAvailability\"\n  [isNamedDashboard]=\"isNamedDashboard\"\n  (dashboardSaved)=\"reloadDashboard($event)\"\n  (previewChanged)=\"dashboardPreview($event)\"\n></c8y-dashboard-detail>\n\n<c8y-widgets-dashboard\n  [context]=\"context\"\n  [contextDashboard]=\"dashboard\"\n  [widgets]=\"widgets\"\n  [isCopyDisabled]=\"isCopyDisabled\"\n  [settings]=\"{\n    isLoading: isLoading,\n    isDisabled: disabled,\n    canDelete: canDelete && !!dashboard,\n    translateWidgetTitle: dashboard?.translateWidgetTitle ?? translateWidgetTitle,\n    allowFullscreen: moduleConfig.allowFullscreen,\n    title: setTitle ? dashboard?.name || title : undefined,\n    widgetMargin: dashboard?.widgetMargin,\n    canCopy: canCopy && !!dashboard,\n    defaultWidth: dashboard?.columns >= 24 ? 8 : 4,\n    columns: dashboard?.columns || 12\n  }\"\n  [breadcrumb]=\"breadcrumbSettings\"\n  [editModeButtons]=\"{\n    undoButtonDisabled: editModeService.undoButtonDisabled,\n    changeToUndoName: editModeService.changeToUndoName,\n    redoButtonDisabled: !editModeService.redoStackLastItem,\n    changeToRedoName: editModeService.redoStackLastItem?.name\n  }\"\n  (onChangeDashboard)=\"positionOrWidgetDimensionChange($event)\"\n  (onAddWidget)=\"addWidget()\"\n  (onEditWidget)=\"editWidget($event)\"\n  (onDeleteWidget)=\"deleteWidget($event)\"\n  (onSaveDashboard)=\"saveWidgetsToDashboard()\"\n  (onCancelDashboard)=\"cancelEditMode()\"\n  (revertChange)=\"revertChange($event)\"\n  (onChangeStart)=\"addDashboardClassToBody()\"\n  (onChangeEnd)=\"removeDashboardClassFromBody()\"\n  (onEditDashboard)=\"editDashboard()\"\n  (onCopyDashboard)=\"copyDashboard()\"\n  (onDeleteDashboard)=\"deleteDashboard()\"\n></c8y-widgets-dashboard>\n" }]
        }], ctorParameters: () => [{ type: i1.ActivatedRoute }, { type: i1.Router }, { type: ContextDashboardService }, { type: i2.AlertService }, { type: i0.Renderer2 }, { type: undefined, decorators: [{
                    type: Inject,
                    args: [CONTEXT_DASHBOARD_CONFIG]
                }] }, { type: WidgetService }, { type: i2.BottomDrawerService }, { type: i2.GainsightService }, { type: i2.ActionBarService }, { type: i2$1.TranslateService }, { type: i2.ModalService }, { type: DashboardEditModeService }], propDecorators: { name: [{
                type: Input
            }], childrenClasses: [{
                type: Input
            }], context: [{
                type: Input
            }], setTitle: [{
                type: Input
            }], defaultWidgets: [{
                type: Input
            }], _canCopy: [{
                type: Input,
                args: ['canCopy']
            }], canDelete: [{
                type: Input
            }], isLoading: [{
                type: Input
            }], breadcrumbSettings: [{
                type: Input
            }], showContextHelpButton: [{
                type: Input
            }], translateWidgetTitle: [{
                type: Input
            }], hideAvailability: [{
                type: Input
            }], class: [{
                type: HostBinding,
                args: ['class']
            }], dashboardDetails: [{
                type: ViewChild,
                args: [DashboardDetailComponent, { static: true }]
            }], widgetsDashboard: [{
                type: ViewChild,
                args: [WidgetsDashboardComponent, { static: true }]
            }], beforeUnloadHandler: [{
                type: HostListener,
                args: ['window:beforeunload', ['$event']]
            }], convertStringToHash: [] } });

class DashboardDetailsTabsFactory {
    constructor() {
        this.activatedRoute = inject(ActivatedRoute);
        this.contextRouteService = inject(ContextRouteService);
        this.generalTab = {
            label: gettext('General'),
            featureId: DashboardDetailsTabId.GENERAL,
            icon: 'imac-settings',
            priority: 100,
            path: [
                {
                    outlets: {
                        [DASHBOARD_DETAILS_OUTLET]: DashboardDetailsTabId.GENERAL
                    }
                }
            ],
            tabsOutlet: DASHBOARD_DETAILS_TABS_OUTLET_NAME
        };
        this.appearanceTab = {
            label: gettext('Appearance'),
            featureId: DashboardDetailsTabId.APPEARANCE,
            icon: 'web-design',
            priority: 50,
            path: [
                {
                    outlets: {
                        [DASHBOARD_DETAILS_OUTLET]: DashboardDetailsTabId.APPEARANCE
                    }
                }
            ],
            tabsOutlet: DASHBOARD_DETAILS_TABS_OUTLET_NAME
        };
        this.versionHistoryTab = {
            label: gettext('Version history'),
            featureId: DashboardDetailsTabId.VERSIONHISTORY,
            icon: 'versions',
            priority: 10,
            path: [
                {
                    outlets: {
                        [DASHBOARD_DETAILS_OUTLET]: DashboardDetailsTabId.VERSIONHISTORY
                    }
                }
            ],
            tabsOutlet: DASHBOARD_DETAILS_TABS_OUTLET_NAME
        };
    }
    get() {
        const rootContext = this.contextRouteService.getRootContextOfChild(this.activatedRoute);
        if (rootContext === ViewContext.Dashboard) {
            return [this.generalTab, this.appearanceTab, this.versionHistoryTab];
        }
        return [];
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DashboardDetailsTabsFactory, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
    static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DashboardDetailsTabsFactory }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DashboardDetailsTabsFactory, decorators: [{
            type: Injectable
        }] });

class PasteDashboardActionComponent {
    constructor(vcRef, router, contextDashboardService, inventoryService) {
        this.vcRef = vcRef;
        this.router = router;
        this.contextDashboardService = contextDashboardService;
        this.inventoryService = inventoryService;
        this.pasteDisabled = false;
        this.dashboardName = '';
    }
    async ngOnInit() {
        this.currentUrl = this.router.url;
        this.contextId = this.currentUrl.match(/\d+/)[0];
        const isDevice = new RegExp(/device\/\d+/).test(this.currentUrl);
        const isGroup = new RegExp(/group\/\d+/).test(this.currentUrl);
        this.currentViewContext = isDevice ? ViewContext.Device : isGroup ? ViewContext.Group : null;
        await this.checkIfPasteIsAllowed();
        this.vcRef.createEmbeddedView(this.templatePaste);
        const clipboard = this.contextDashboardService.copyClipboard;
        if (clipboard) {
            this.dashboardName = clipboard.dashboard.name;
        }
    }
    async pasteDashboard() {
        if (!this.contextMO) {
            this.contextMO = await this.getContextMo(this.contextId);
        }
        const context = {
            context: this.currentViewContext,
            contextData: this.contextMO
        };
        await this.contextDashboardService.pasteDashboard(context);
    }
    async getContextMo(id) {
        return (await this.inventoryService.detail(id)).data;
    }
    async checkIfPasteIsAllowed() {
        const clipboardViewContext = this.contextDashboardService.copyClipboard.context.context;
        const isTypeDashboard = this.contextDashboardService.copyClipboard?.dashboard?.deviceType;
        if (this.currentViewContext !== clipboardViewContext) {
            if (clipboardViewContext === ViewContext.Device) {
                this.buttonTitle = gettext('Device dashboards can only be copied into a device.');
            }
            else if (clipboardViewContext === ViewContext.Group) {
                this.buttonTitle = gettext('Group dashboards can only be copied into a group.');
            }
            this.pasteDisabled = true;
            return;
        }
        else if (isTypeDashboard) {
            this.contextMO = await this.getContextMo(this.contextId);
            const dashboardDeviceType = this.contextDashboardService.copyClipboard.dashboard.deviceTypeValue;
            if (dashboardDeviceType === this.contextMO.type) {
                this.buttonTitle = gettext('Dashboard template can only be copied into assets of a different type.');
                this.pasteDisabled = true;
                return;
            }
        }
        this.buttonTitle = gettext('Paste dashboard');
        this.pasteDisabled = false;
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: PasteDashboardActionComponent, deps: [{ token: i0.ViewContainerRef }, { token: i1.Router }, { token: ContextDashboardService }, { token: i1$1.InventoryService }], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.14", type: PasteDashboardActionComponent, isStandalone: true, selector: "c8y-paste-dashboard-action", viewQueries: [{ propertyName: "templatePaste", first: true, predicate: ["templatePaste"], descendants: true, read: TemplateRef, static: true }], ngImport: i0, template: `
    <ng-template #templatePaste>
      <li>
        <button
          class="btn btn-link"
          title="{{ buttonTitle | translate }}"
          type="button"
          (click)="pasteDashboard()"
          [disabled]="pasteDisabled"
        >
          <i class="m-r-4" c8yIcon="clipboard"></i>
          <span ngNonBindable translate [translateParams]="{ dashboardName: dashboardName }">
            Paste dashboard "{{ dashboardName }}"
          </span>
        </button>
      </li>
    </ng-template>
  `, isInline: true, dependencies: [{ kind: "directive", type: IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "directive", type: C8yTranslateDirective, selector: "[translate],[ngx-translate]" }, { kind: "pipe", type: C8yTranslatePipe, name: "translate" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: PasteDashboardActionComponent, decorators: [{
            type: Component,
            args: [{
                    selector: 'c8y-paste-dashboard-action',
                    template: `
    <ng-template #templatePaste>
      <li>
        <button
          class="btn btn-link"
          title="{{ buttonTitle | translate }}"
          type="button"
          (click)="pasteDashboard()"
          [disabled]="pasteDisabled"
        >
          <i class="m-r-4" c8yIcon="clipboard"></i>
          <span ngNonBindable translate [translateParams]="{ dashboardName: dashboardName }">
            Paste dashboard "{{ dashboardName }}"
          </span>
        </button>
      </li>
    </ng-template>
  `,
                    imports: [IconDirective, C8yTranslateDirective, C8yTranslatePipe]
                }]
        }], ctorParameters: () => [{ type: i0.ViewContainerRef }, { type: i1.Router }, { type: ContextDashboardService }, { type: i1$1.InventoryService }], propDecorators: { templatePaste: [{
                type: ViewChild,
                args: ['templatePaste', { read: TemplateRef, static: true }]
            }] } });

class TypeDashboardInfoComponent {
    constructor(inventory, alert) {
        this.inventory = inventory;
        this.alert = alert;
    }
    async ngOnInit() {
        await this.updateDeviceTypeCount();
    }
    async updateDeviceTypeCount() {
        try {
            const count = await this.inventory.count({
                type: this.deviceTypeValue || this.context.c8y_Dashboard.deviceTypeValue
            });
            this.deviceTypeInstancesCount = count.data;
            this.asyncRenderSuccess?.();
        }
        catch (error) {
            this.alert.addServerFailure(error);
            this.asyncRenderFail?.();
        }
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: TypeDashboardInfoComponent, deps: [{ token: i1$1.InventoryService }, { token: i2.AlertService }], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.14", type: TypeDashboardInfoComponent, isStandalone: true, selector: "c8y-type-dashboard-info", inputs: { deviceTypeValue: "deviceTypeValue", displayDeviceTypeValue: "displayDeviceTypeValue", context: "context", asyncRenderSuccess: "asyncRenderSuccess", asyncRenderFail: "asyncRenderFail" }, ngImport: i0, template: "<ul class=\"list-unstyled small animated fadeIn\">\n  <li class=\"p-t-4 p-b-4 d-flex separator-top-bottom text-nowrap\">\n    <label class=\"small m-b-0 m-r-8\">{{ 'Target asset model' | translate }}</label>\n    <span class=\"m-l-auto\">\n      <span class=\"label label-info\">\n        {{ displayDeviceTypeValue || context.c8y_Dashboard.deviceTypeValue }}\n      </span>\n    </span>\n  </li>\n  <li class=\"p-t-4 p-b-4 d-flex separator-bottom text-nowrap\">\n    <label class=\"small m-b-0 m-r-8\">{{ 'Dashboard instances' | translate }}</label>\n    <span class=\"m-l-auto\">{{ deviceTypeInstancesCount }}</span>\n  </li>\n  <ng-container *ngIf=\"context?.c8y_Dashboard\">\n    <li class=\"p-t-4 p-b-4 d-flex separator-bottom text-nowrap\">\n      <label class=\"small m-b-0 m-r-8\">{{ 'Created' | translate }}</label>\n      <span class=\"m-l-auto\">\n        {{ context.creationTime | c8yDate }} {{ 'by`user`' | translate }} {{ context.owner }}\n      </span>\n    </li>\n    <li\n      class=\"p-t-4 p-b-4 d-flex separator-bottom text-nowrap\"\n      *ngIf=\"context?.c8y_DashboardHistory?.length\"\n    >\n      <label class=\"small m-b-0 m-r-8\">{{ 'Last modified' | translate }}</label>\n      <span class=\"m-l-auto\">\n        {{ context.c8y_DashboardHistory[0].created | c8yDate }} {{ 'by`user`' | translate }}\n        {{ context.c8y_DashboardHistory[0].author }}\n      </span>\n    </li>\n  </ng-container>\n</ul>\n", dependencies: [{ kind: "pipe", type: C8yTranslatePipe, name: "translate" }, { kind: "pipe", type: DatePipe, name: "c8yDate" }, { kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: TypeDashboardInfoComponent, decorators: [{
            type: Component,
            args: [{ selector: 'c8y-type-dashboard-info', standalone: true, imports: [C8yTranslatePipe, DatePipe, NgIf], template: "<ul class=\"list-unstyled small animated fadeIn\">\n  <li class=\"p-t-4 p-b-4 d-flex separator-top-bottom text-nowrap\">\n    <label class=\"small m-b-0 m-r-8\">{{ 'Target asset model' | translate }}</label>\n    <span class=\"m-l-auto\">\n      <span class=\"label label-info\">\n        {{ displayDeviceTypeValue || context.c8y_Dashboard.deviceTypeValue }}\n      </span>\n    </span>\n  </li>\n  <li class=\"p-t-4 p-b-4 d-flex separator-bottom text-nowrap\">\n    <label class=\"small m-b-0 m-r-8\">{{ 'Dashboard instances' | translate }}</label>\n    <span class=\"m-l-auto\">{{ deviceTypeInstancesCount }}</span>\n  </li>\n  <ng-container *ngIf=\"context?.c8y_Dashboard\">\n    <li class=\"p-t-4 p-b-4 d-flex separator-bottom text-nowrap\">\n      <label class=\"small m-b-0 m-r-8\">{{ 'Created' | translate }}</label>\n      <span class=\"m-l-auto\">\n        {{ context.creationTime | c8yDate }} {{ 'by`user`' | translate }} {{ context.owner }}\n      </span>\n    </li>\n    <li\n      class=\"p-t-4 p-b-4 d-flex separator-bottom text-nowrap\"\n      *ngIf=\"context?.c8y_DashboardHistory?.length\"\n    >\n      <label class=\"small m-b-0 m-r-8\">{{ 'Last modified' | translate }}</label>\n      <span class=\"m-l-auto\">\n        {{ context.c8y_DashboardHistory[0].created | c8yDate }} {{ 'by`user`' | translate }}\n        {{ context.c8y_DashboardHistory[0].author }}\n      </span>\n    </li>\n  </ng-container>\n</ul>\n" }]
        }], ctorParameters: () => [{ type: i1$1.InventoryService }, { type: i2.AlertService }], propDecorators: { deviceTypeValue: [{
                type: Input
            }], displayDeviceTypeValue: [{
                type: Input
            }], context: [{
                type: Input
            }], asyncRenderSuccess: [{
                type: Input
            }], asyncRenderFail: [{
                type: Input
            }] } });

class WidgetPreviewWrapperComponent {
    constructor() {
        this.widgetConfigService = inject(WidgetConfigService);
    }
    set previewSet(template) {
        if (template) {
            this.widgetConfigService.setPreview(template);
            return;
        }
        this.widgetConfigService.setPreview(null);
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: WidgetPreviewWrapperComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.14", type: WidgetPreviewWrapperComponent, isStandalone: true, selector: "c8y-widget-preview-wrapper", viewQueries: [{ propertyName: "previewSet", first: true, predicate: ["previewContent"], descendants: true }], ngImport: i0, template: '<ng-template #previewContent><ng-content></ng-content></ng-template>', isInline: true }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: WidgetPreviewWrapperComponent, decorators: [{
            type: Component,
            args: [{
                    selector: 'c8y-widget-preview-wrapper',
                    template: '<ng-template #previewContent><ng-content></ng-content></ng-template>',
                    standalone: true,
                    imports: []
                }]
        }], propDecorators: { previewSet: [{
                type: ViewChild,
                args: ['previewContent']
            }] } });

const defaultConfig = {
    allowFullscreen: true
};
/**
 * Module for the Context Dashboard feature.
 * This module provides components and configurations for managing dashboards,
 * {@link ContextDashboardComponent} is the main component for displaying the dashboard context.
 * {@link WidgetConfigComponent} allows for configuring widgets within the dashboard.
 * {@link DashboardDetailComponent} provides detailed views of individual dashboards.
 */
class ContextDashboardModule {
    static config(config = {}) {
        return {
            ngModule: ContextDashboardModule,
            providers: [
                {
                    provide: CONTEXT_DASHBOARD_CONFIG,
                    useValue: { ...defaultConfig, ...config }
                },
                hookRoute(config.routes || [])
            ]
        };
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ContextDashboardModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
    static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.14", ngImport: i0, type: ContextDashboardModule, imports: [CoreModule,
            TooltipModule,
            PopoverModule,
            AssetSelectorModule,
            IconSelectorModule, i1$3.BsDropdownModule, i2$3.CollapseModule, RouterModule,
            AppearanceSettingsComponent,
            TypeDashboardInfoComponent,
            WidgetPreviewComponent,
            WidgetConfigSectionComponent,
            WidgetPreviewWrapperComponent,
            ContextDashboardComponent,
            WidgetConfigComponent,
            DashboardDetailComponent,
            AddDashboardComponent,
            PasteDashboardActionComponent], exports: [ContextDashboardComponent,
            WidgetConfigComponent,
            DashboardDetailComponent,
            TypeDashboardInfoComponent,
            WidgetConfigSectionComponent,
            AppearanceSettingsComponent,
            WidgetPreviewWrapperComponent] }); }
    static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ContextDashboardModule, providers: [
            {
                provide: CONTEXT_DASHBOARD_CONFIG,
                useValue: defaultConfig
            },
            hookTab(DashboardDetailsTabsFactory),
            hookRoute([
                {
                    path: DashboardDetailsTabId.GENERAL,
                    loadComponent: () => import('./c8y-ngx-components-context-dashboard-dashboard-general-settings.component-RXvOcs04.mjs').then(m => m.DashboardGeneralSettingsComponent),
                    outlet: DASHBOARD_DETAILS_OUTLET,
                    context: ViewContext.Dashboard
                },
                {
                    path: DashboardDetailsTabId.APPEARANCE,
                    loadComponent: () => import('./c8y-ngx-components-context-dashboard-dashboard-appearance-settings.component-C-JLbcn5.mjs').then(m => m.DashboardAppearanceSettingsComponent),
                    outlet: DASHBOARD_DETAILS_OUTLET,
                    context: ViewContext.Dashboard
                },
                {
                    path: DashboardDetailsTabId.VERSIONHISTORY,
                    loadComponent: () => import('./c8y-ngx-components-context-dashboard-dashboard-version-history.component-DmPIJGXO.mjs').then(m => m.DashboardVersionHistoryComponent),
                    outlet: DASHBOARD_DETAILS_OUTLET,
                    context: ViewContext.Dashboard
                }
            ])
        ], imports: [CoreModule,
            TooltipModule,
            PopoverModule,
            AssetSelectorModule,
            IconSelectorModule,
            BsDropdownModule.forRoot(),
            CollapseModule.forRoot(),
            RouterModule,
            AppearanceSettingsComponent,
            WidgetPreviewComponent,
            WidgetConfigSectionComponent,
            ContextDashboardComponent,
            WidgetConfigComponent,
            DashboardDetailComponent] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ContextDashboardModule, decorators: [{
            type: NgModule,
            args: [{
                    imports: [
                        CoreModule,
                        TooltipModule,
                        PopoverModule,
                        AssetSelectorModule,
                        IconSelectorModule,
                        BsDropdownModule.forRoot(),
                        CollapseModule.forRoot(),
                        RouterModule,
                        AppearanceSettingsComponent,
                        TypeDashboardInfoComponent,
                        WidgetPreviewComponent,
                        WidgetConfigSectionComponent,
                        WidgetPreviewWrapperComponent,
                        ContextDashboardComponent,
                        WidgetConfigComponent,
                        DashboardDetailComponent,
                        AddDashboardComponent,
                        PasteDashboardActionComponent
                    ],
                    exports: [
                        ContextDashboardComponent,
                        WidgetConfigComponent,
                        DashboardDetailComponent,
                        TypeDashboardInfoComponent,
                        WidgetConfigSectionComponent,
                        AppearanceSettingsComponent,
                        WidgetPreviewWrapperComponent
                    ],
                    providers: [
                        {
                            provide: CONTEXT_DASHBOARD_CONFIG,
                            useValue: defaultConfig
                        },
                        hookTab(DashboardDetailsTabsFactory),
                        hookRoute([
                            {
                                path: DashboardDetailsTabId.GENERAL,
                                loadComponent: () => import('./c8y-ngx-components-context-dashboard-dashboard-general-settings.component-RXvOcs04.mjs').then(m => m.DashboardGeneralSettingsComponent),
                                outlet: DASHBOARD_DETAILS_OUTLET,
                                context: ViewContext.Dashboard
                            },
                            {
                                path: DashboardDetailsTabId.APPEARANCE,
                                loadComponent: () => import('./c8y-ngx-components-context-dashboard-dashboard-appearance-settings.component-C-JLbcn5.mjs').then(m => m.DashboardAppearanceSettingsComponent),
                                outlet: DASHBOARD_DETAILS_OUTLET,
                                context: ViewContext.Dashboard
                            },
                            {
                                path: DashboardDetailsTabId.VERSIONHISTORY,
                                loadComponent: () => import('./c8y-ngx-components-context-dashboard-dashboard-version-history.component-DmPIJGXO.mjs').then(m => m.DashboardVersionHistoryComponent),
                                outlet: DASHBOARD_DETAILS_OUTLET,
                                context: ViewContext.Dashboard
                            }
                        ])
                    ]
                }]
        }] });

class DashboardActionBarFactory {
    constructor() {
        this.contextDashboardService = inject(ContextDashboardService);
    }
    get(activeRoute) {
        const actions = [];
        const data = !activeRoute.parent || activeRoute.snapshot.data.context
            ? activeRoute.snapshot.data
            : activeRoute.parent.snapshot.data;
        const { contextData } = data;
        const canPaste = contextData &&
            data.context === this.targetContext &&
            this.contextDashboardService.copyClipboard;
        if (canPaste) {
            actions.push({
                priority: -10,
                placement: 'more',
                template: PasteDashboardActionComponent
            });
        }
        return actions;
    }
}

class DeviceInfoDashboardComponent {
    constructor(router, widgetService, translateService, inventoryService) {
        this.router = router;
        this.widgetService = widgetService;
        this.translateService = translateService;
        this.inventoryService = inventoryService;
        this.isLoading = true;
        this.dashboardName = '';
        this.DEVICE_INFO_WIDGETS = [
            {
                name: 'deviceStatusWidget',
                title: gettext('Device status'),
                _x: 0,
                _y: 0,
                _height: 5,
                _width: 8
            },
            {
                name: 'Data points graph',
                title: gettext('Device and communication'),
                _x: 8,
                _y: 0,
                _height: 5,
                _width: 16,
                config: {
                    interval: 'hours',
                    aggregation: 'NONE',
                    realtime: true,
                    datapointsInitialDisplayLimit: 3,
                    datapoints: [
                        {
                            fragment: 'c8y_Battery',
                            series: 'level',
                            label: this.translateService.instant('Battery: level'),
                            unit: '%',
                            renderType: 'min',
                            lineType: 'line',
                            color: '#5e07b3',
                            __active: true
                        },
                        {
                            fragment: 'c8y_SignalStrength',
                            series: 'rssi',
                            label: this.translateService.instant('Signal: RSSI'),
                            unit: 'dBm',
                            renderType: 'min',
                            lineType: 'line',
                            color: '#a7d0f1',
                            __active: true
                        },
                        {
                            fragment: 'c8y_MemoryMeasurement',
                            series: 'Used',
                            label: this.translateService.instant('Memory: used'),
                            unit: 'MB',
                            renderType: 'min',
                            lineType: 'line',
                            color: '#f75229',
                            __active: true
                        },
                        {
                            fragment: 'c8y_CPUMeasurement',
                            series: 'Workload',
                            label: this.translateService.instant('CPU: workload'),
                            unit: '%',
                            renderType: 'min',
                            lineType: 'line',
                            color: '#b24f3e',
                            __active: true
                        },
                        {
                            fragment: 'c8y_NetworkStatistics',
                            series: 'Upload',
                            label: this.translateService.instant('Network: upload'),
                            unit: 'KB/s',
                            renderType: 'min',
                            lineType: 'line',
                            color: '#88c039',
                            __active: true
                        },
                        {
                            fragment: 'c8y_SignalStrength',
                            series: 'RCSP',
                            label: this.translateService.instant('Signal: RCSP'),
                            renderType: 'min',
                            lineType: 'line',
                            color: '#5e2f19',
                            __active: true
                        },
                        {
                            fragment: 'c8y_SignalStrength',
                            series: 'RSCP',
                            label: this.translateService.instant('Signal: RSCP'),
                            renderType: 'min',
                            lineType: 'line',
                            color: '#5e2f19',
                            __active: true
                        },
                        {
                            fragment: 'c8y_SignalStrength',
                            series: 'RSRP',
                            label: this.translateService.instant('Signal: RSRP'),
                            renderType: 'min',
                            lineType: 'line',
                            color: '#43ba3b',
                            __active: true
                        },
                        {
                            fragment: 'c8y_SignalStrength',
                            series: 'RSRQ',
                            label: this.translateService.instant('Signal: RSRQ'),
                            renderType: 'min',
                            lineType: 'line',
                            color: '#14b0b3',
                            __active: true
                        },
                        {
                            fragment: 'c8y_SignalStrength',
                            series: 'ber',
                            label: this.translateService.instant('Signal: BER'),
                            unit: '%',
                            renderType: 'min',
                            lineType: 'line',
                            color: '#44ff33',
                            __active: true
                        },
                        {
                            fragment: 'c8y_SignalStrength',
                            series: 'ECN0',
                            label: this.translateService.instant('Signal: ECN0'),
                            renderType: 'min',
                            lineType: 'line',
                            color: '#873efa',
                            __active: true
                        },
                        {
                            fragment: 'c8y_NetworkStatistics',
                            series: 'Download',
                            label: this.translateService.instant('Network: download'),
                            unit: 'KB/s',
                            renderType: 'min',
                            lineType: 'line',
                            color: '#f27a45',
                            __active: true
                        },
                        {
                            fragment: 'c8y_MemoryMeasurement',
                            series: 'Total',
                            label: this.translateService.instant('Memory: total'),
                            unit: 'MB',
                            renderType: 'min',
                            lineType: 'line',
                            color: '#dadd8c',
                            __active: true
                        }
                    ],
                    alarmsEventsConfigs: [
                        {
                            label: 'c8y_LocationUpdate',
                            timelineType: 'EVENT',
                            color: '#6f6cd0',
                            filters: {
                                type: 'c8y_LocationUpdate'
                            },
                            __active: true
                        },
                        {
                            label: 'c8y_UnavailabilityAlarm',
                            timelineType: 'ALARM',
                            color: '#375bf2',
                            filters: {
                                type: 'c8y_UnavailabilityAlarm'
                            },
                            __active: true
                        }
                    ]
                }
            },
            {
                name: 'Asset Properties',
                title: gettext('Device data'),
                config: {
                    options: {
                        properties: [
                            {
                                id: 'c8ySchema!!id',
                                __active: true,
                                keyPath: ['id'],
                                type: 'string',
                                label: 'ID'
                            },
                            {
                                id: 'c8ySchema!!name',
                                __active: true,
                                keyPath: ['name'],
                                type: 'string',
                                label: 'Name'
                            },
                            {
                                id: 'c8ySchema!!type',
                                __active: true,
                                keyPath: ['type'],
                                type: 'string',
                                label: 'Type'
                            },
                            {
                                id: 'c8ySchema!!lastUpdated',
                                __active: true,
                                keyPath: ['lastUpdated'],
                                type: 'string',
                                label: 'Last Updated'
                            },
                            {
                                id: 'c8ySchema!!creationTime',
                                __active: true,
                                keyPath: ['creationTime'],
                                type: 'string',
                                label: 'Creation Time'
                            }
                        ],
                        allowDisplayAndEditKnownProperties: false,
                        hiddenPropertiesKeyPaths: ['owner']
                    }
                },
                _x: 0,
                _y: 5,
                _height: 8,
                _width: 8
            },
            {
                name: 'Alarm list',
                title: gettext('Active, critical alarms'),
                _x: 8,
                _y: 5,
                _height: 4,
                _width: 8,
                config: {
                    options: {
                        orderMode: 'ACTIVE_FIRST',
                        severity: { CRITICAL: true },
                        status: {
                            ACTIVE: true,
                            ACKNOWLEDGED: false,
                            CLEARED: false
                        },
                        types: []
                    }
                }
            },
            {
                name: 'deviceGroupAssignmentWidget',
                title: gettext('Group assignment'),
                _x: 16,
                _y: 5,
                _height: 4,
                _width: 8
            },
            {
                componentId: 'Map',
                title: gettext('Location'),
                _x: 8,
                _y: 9,
                _height: 4,
                _width: 16
            }
        ];
    }
    ngOnInit() {
        this.contextSubscription = this.router.parent.data
            .pipe(tap(({ contextData }) => {
            const contextNameValid = contextData.name && contextData.name.trim();
            this.title = contextNameValid ? contextData.name : contextData.id;
            this.dashboardName = `${contextData.id}-device-info`;
        }))
            .subscribe(async (data) => {
            this.currentContext = data;
            this.addDeviceTargetForDP(data.contextData.id, data.contextData.name);
        });
        this.widgetsSubscription = from(this.DEVICE_INFO_WIDGETS)
            .pipe(mergeMap(widget => this.widgetService.mapLegacy(widget)), map(widget => {
            let device = this.router.parent.snapshot.data.contextData;
            if (device) {
                device = {
                    name: device.name,
                    id: device.id
                };
            }
            return {
                ...widget,
                config: {
                    ...widget.config,
                    device
                }
            };
        }), toArray())
            .subscribe(async (data) => {
            await this.filterSupportedSeriesForDP(this.currentContext.contextData.id, data);
            this.widgetsList = data;
            this.isLoading = false;
        });
    }
    addDeviceTargetForDP(id, name) {
        const dpWidget = this.DEVICE_INFO_WIDGETS.filter(item => item.name == 'Data points graph')[0];
        // Add target to dataPoint config
        dpWidget.config.datapoints.map(item => (item['__target'] = { id, name }));
        // Add target to alarmEvents Config
        dpWidget.config.alarmsEventsConfigs.map(item => (item['__target'] = { id, name }));
    }
    async filterSupportedSeriesForDP(id, widgets) {
        const supportedMeasurements = await this.inventoryService.getMeasurementsAndSeries(id);
        const isDataPointSupported = dp => supportedMeasurements.some(({ fragment, series }) => dp.fragment === fragment && dp.series === series);
        const dpWidget = widgets.find(({ name }) => name === 'Data points graph');
        dpWidget.config.datapoints = dpWidget.config.datapoints.filter(isDataPointSupported);
    }
    ngOnDestroy() {
        this.widgetsSubscription.unsubscribe();
        this.contextSubscription.unsubscribe();
    }
    canDeactivate() {
        return this.contextDashboard?.canDeactivate();
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DeviceInfoDashboardComponent, deps: [{ token: i1.ActivatedRoute }, { token: WidgetService }, { token: i2$1.TranslateService }, { token: i1$1.InventoryService }], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.14", type: DeviceInfoDashboardComponent, isStandalone: true, selector: "c8y-device-info-dashboard-dashboard", viewQueries: [{ propertyName: "contextDashboard", first: true, predicate: ContextDashboardComponent, descendants: true }], ngImport: i0, template: "<div *ngIf=\"!isLoading\">\n  <c8y-title>\n    {{ title }}\n  </c8y-title>\n  <c8y-context-dashboard\n    [translateWidgetTitle]=\"true\"\n    [name]=\"dashboardName\"\n    [context]=\"currentContext\"\n    [defaultWidgets]=\"widgetsList\"\n    [canDelete]=\"false\"\n    [hideAvailability]=\"true\"\n    [canCopy]=\"false\"\n  ></c8y-context-dashboard>\n</div>\n", dependencies: [{ kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: TitleComponent, selector: "c8y-title", inputs: ["pageTitleUpdate"] }, { kind: "component", type: ContextDashboardComponent, selector: "c8y-context-dashboard", inputs: ["name", "childrenClasses", "context", "setTitle", "defaultWidgets", "canCopy", "canDelete", "isLoading", "breadcrumbSettings", "showContextHelpButton", "translateWidgetTitle", "hideAvailability"] }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DeviceInfoDashboardComponent, decorators: [{
            type: Component,
            args: [{ selector: 'c8y-device-info-dashboard-dashboard', imports: [NgIf, TitleComponent, ContextDashboardComponent], template: "<div *ngIf=\"!isLoading\">\n  <c8y-title>\n    {{ title }}\n  </c8y-title>\n  <c8y-context-dashboard\n    [translateWidgetTitle]=\"true\"\n    [name]=\"dashboardName\"\n    [context]=\"currentContext\"\n    [defaultWidgets]=\"widgetsList\"\n    [canDelete]=\"false\"\n    [hideAvailability]=\"true\"\n    [canCopy]=\"false\"\n  ></c8y-context-dashboard>\n</div>\n" }]
        }], ctorParameters: () => [{ type: i1.ActivatedRoute }, { type: WidgetService }, { type: i2$1.TranslateService }, { type: i1$1.InventoryService }], propDecorators: { contextDashboard: [{
                type: ViewChild,
                args: [ContextDashboardComponent, { static: false }]
            }] } });

class DeviceInfoDashboardModule {
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DeviceInfoDashboardModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
    static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.14", ngImport: i0, type: DeviceInfoDashboardModule, imports: [CommonModule, CoreModule, ContextDashboardModule, DeviceInfoDashboardComponent] }); }
    static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DeviceInfoDashboardModule, providers: [
            hookRoute({
                context: ViewContext.Device,
                path: 'device-info',
                component: DeviceInfoDashboardComponent,
                label: gettext('Info'),
                priority: 10000,
                icon: 'asterisk',
                canDeactivate: [(component) => component.canDeactivate()],
                rootContext: ViewContext.Dashboard
            })
        ], imports: [CommonModule, CoreModule, ContextDashboardModule, DeviceInfoDashboardComponent] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DeviceInfoDashboardModule, decorators: [{
            type: NgModule,
            args: [{
                    imports: [CommonModule, CoreModule, ContextDashboardModule, DeviceInfoDashboardComponent],
                    exports: [],
                    providers: [
                        hookRoute({
                            context: ViewContext.Device,
                            path: 'device-info',
                            component: DeviceInfoDashboardComponent,
                            label: gettext('Info'),
                            priority: 10000,
                            icon: 'asterisk',
                            canDeactivate: [(component) => component.canDeactivate()],
                            rootContext: ViewContext.Dashboard
                        })
                    ]
                }]
        }] });

class DeviceManagementHomeDashboardComponent {
    constructor(widgetService) {
        this.widgetService = widgetService;
        this.widgets = [];
        this.dashboardName = 'home-dm1';
        this.defaultWidgets = [
            {
                name: defaultWidgetIds.DEVICE_MANAGEMENT_WELCOME,
                title: gettext('Quick links'),
                _x: 0,
                _y: 0,
                _height: 4,
                _width: 12
            },
            {
                name: 'Recent Alarms',
                title: gettext('Recent alarms'),
                _x: 12,
                _y: 0,
                _height: 4,
                _width: 12
            }
        ];
    }
    ngOnInit() {
        this.defaultWidgets.map(async (widget) => {
            await this.widgetService
                .mapLegacy(widget)
                .then(transformedWidget => this.widgets.push(transformedWidget));
        });
    }
    canDeactivate() {
        return this.contextDashboard?.canDeactivate();
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DeviceManagementHomeDashboardComponent, deps: [{ token: WidgetService }], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.14", type: DeviceManagementHomeDashboardComponent, isStandalone: true, selector: "c8y-device-management-home-dashboard", viewQueries: [{ propertyName: "contextDashboard", first: true, predicate: ContextDashboardComponent, descendants: true, static: true }], ngImport: i0, template: "<c8y-title>\n  {{ 'Home' | translate }}\n</c8y-title>\n\n<c8y-context-dashboard\n  [name]=\"dashboardName\"\n  [defaultWidgets]=\"defaultWidgets\"\n  [canDelete]=\"false\"\n  [hideAvailability]=\"true\"\n></c8y-context-dashboard>\n", dependencies: [{ kind: "component", type: TitleComponent, selector: "c8y-title", inputs: ["pageTitleUpdate"] }, { kind: "component", type: ContextDashboardComponent, selector: "c8y-context-dashboard", inputs: ["name", "childrenClasses", "context", "setTitle", "defaultWidgets", "canCopy", "canDelete", "isLoading", "breadcrumbSettings", "showContextHelpButton", "translateWidgetTitle", "hideAvailability"] }, { kind: "pipe", type: C8yTranslatePipe, name: "translate" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DeviceManagementHomeDashboardComponent, decorators: [{
            type: Component,
            args: [{ selector: 'c8y-device-management-home-dashboard', imports: [TitleComponent, ContextDashboardComponent, C8yTranslatePipe], template: "<c8y-title>\n  {{ 'Home' | translate }}\n</c8y-title>\n\n<c8y-context-dashboard\n  [name]=\"dashboardName\"\n  [defaultWidgets]=\"defaultWidgets\"\n  [canDelete]=\"false\"\n  [hideAvailability]=\"true\"\n></c8y-context-dashboard>\n" }]
        }], ctorParameters: () => [{ type: WidgetService }], propDecorators: { contextDashboard: [{
                type: ViewChild,
                args: [ContextDashboardComponent, { static: true }]
            }] } });

class DeviceManagementHomeDashboardModule {
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DeviceManagementHomeDashboardModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
    static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.14", ngImport: i0, type: DeviceManagementHomeDashboardModule, imports: [CommonModule,
            CoreModule,
            ContextDashboardModule,
            DeviceManagementHomeDashboardComponent] }); }
    static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DeviceManagementHomeDashboardModule, providers: [
            hookRoute({
                path: 'home',
                component: DeviceManagementHomeDashboardComponent,
                canDeactivate: [(component) => component.canDeactivate()],
                rootContext: ViewContext.Dashboard
            }),
            hookNavigator({ label: gettext('Home'), icon: 'home', priority: 10000, path: '/home' })
        ], imports: [CommonModule,
            CoreModule,
            ContextDashboardModule,
            DeviceManagementHomeDashboardComponent] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DeviceManagementHomeDashboardModule, decorators: [{
            type: NgModule,
            args: [{
                    imports: [
                        CommonModule,
                        CoreModule,
                        ContextDashboardModule,
                        DeviceManagementHomeDashboardComponent
                    ],
                    exports: [],
                    providers: [
                        hookRoute({
                            path: 'home',
                            component: DeviceManagementHomeDashboardComponent,
                            canDeactivate: [(component) => component.canDeactivate()],
                            rootContext: ViewContext.Dashboard
                        }),
                        hookNavigator({ label: gettext('Home'), icon: 'home', priority: 10000, path: '/home' })
                    ]
                }]
        }] });

class WidgetConfigAppearanceComponent {
    constructor() {
        this.widgetConfig = inject(WidgetConfigComponent);
        this.possibleStyling = { WIDGET_HEADER_CLASSES, WIDGET_CONTENT_CLASSES };
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: WidgetConfigAppearanceComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.14", type: WidgetConfigAppearanceComponent, isStandalone: true, selector: "c8y-widget-config-appearance", ngImport: i0, template: "<div class=\"row\">\n  <div class=\"col-xs-6\">\n    <c8y-appearance-settings\n      [(themeClass)]=\"widgetConfig.styling.contentClass\"\n      [(headerClass)]=\"widgetConfig.styling.headerClass\"\n      [possibleStylingTheme]=\"possibleStyling.WIDGET_CONTENT_CLASSES\"\n      [possibleStylingHeader]=\"possibleStyling.WIDGET_HEADER_CLASSES\"\n      [defaultThemeClass]=\"widgetConfig.defaultStyling.contentClass\"\n      [defaultHeaderClass]=\"widgetConfig.defaultStyling.headerClass\"\n    ></c8y-appearance-settings>\n  </div>\n  <div class=\"col-xs-6 sticky-header-top-0\">\n    <c8y-widget-preview\n      style=\"height: calc(100vh - 382px)\"\n      [previewClasses]=\"widgetConfig.getStyle(true)\"\n    ></c8y-widget-preview>\n  </div>\n</div>\n", dependencies: [{ kind: "component", type: AppearanceSettingsComponent, selector: "c8y-appearance-settings", inputs: ["themeClass", "headerClass", "defaultThemeClass", "defaultHeaderClass", "dashboardSettings", "possibleStylingTheme", "possibleStylingHeader", "columns"], outputs: ["themeClassChange", "headerClassChange", "onChange"] }, { kind: "component", type: WidgetPreviewComponent, selector: "c8y-widget-preview", inputs: ["previewClasses"] }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: WidgetConfigAppearanceComponent, decorators: [{
            type: Component,
            args: [{ standalone: true, imports: [AppearanceSettingsComponent, NgIf, WidgetPreviewComponent], selector: 'c8y-widget-config-appearance', template: "<div class=\"row\">\n  <div class=\"col-xs-6\">\n    <c8y-appearance-settings\n      [(themeClass)]=\"widgetConfig.styling.contentClass\"\n      [(headerClass)]=\"widgetConfig.styling.headerClass\"\n      [possibleStylingTheme]=\"possibleStyling.WIDGET_CONTENT_CLASSES\"\n      [possibleStylingHeader]=\"possibleStyling.WIDGET_HEADER_CLASSES\"\n      [defaultThemeClass]=\"widgetConfig.defaultStyling.contentClass\"\n      [defaultHeaderClass]=\"widgetConfig.defaultStyling.headerClass\"\n    ></c8y-appearance-settings>\n  </div>\n  <div class=\"col-xs-6 sticky-header-top-0\">\n    <c8y-widget-preview\n      style=\"height: calc(100vh - 382px)\"\n      [previewClasses]=\"widgetConfig.getStyle(true)\"\n    ></c8y-widget-preview>\n  </div>\n</div>\n" }]
        }] });

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

export { ALL_GLOBAL_ROLES_SELECTED, AddDashboardComponent, AddDashboardFactory, AppearanceSettingsComponent, CONTEXT_DASHBOARD_CONFIG, ContextDashboardComponent, ContextDashboardModule, ContextDashboardService, ContextDashboardType, DASHBOARD_CHILDREN_STATE_NAME, DASHBOARD_DETAILS_OUTLET, DASHBOARD_DETAILS_TABS_OUTLET_NAME, DASHBOARD_SETTINGS_CHANGES, DASHBOARD_THEME_CLASSES, DashboardActionBarFactory, DashboardDetailComponent, DashboardDetailService, DashboardDetailsTabId, DeviceInfoDashboardComponent, DeviceInfoDashboardModule, DeviceManagementHomeDashboardComponent, DeviceManagementHomeDashboardModule, HOOK_WIDGET_CONFIG, NewDashboardGuard, PRODUCT_EXPERIENCE, PasteDashboardActionComponent, REPORT_DEFAULT_NAVIGATION_NODE_PRIORITY, STYLING_CLASS_PREFIXES, TypeDashboardInfoComponent, WIDGET_CONTENT_CLASSES, WIDGET_HEADER_CLASSES, WidgetAssetSelectorComponent, WidgetConfigAppearanceComponent, WidgetConfigComponent, WidgetConfigFeedbackComponent, WidgetConfigGeneralComponent, WidgetConfigSectionComponent, WidgetConfigSectionService, WidgetConfigService, WidgetPreviewComponent, WidgetPreviewWrapperComponent, WidgetService, hookWidgetConfig, newDashboardTab };
//# sourceMappingURL=c8y-ngx-components-context-dashboard.mjs.map